1 25 package classycle.dependency; 26 27 import java.io.BufferedReader ; 28 import java.io.IOException ; 29 import java.io.StringReader ; 30 import java.util.ArrayList ; 31 import java.util.Arrays ; 32 import java.util.StringTokenizer ; 33 34 import classycle.util.AndStringPattern; 35 import classycle.util.NotStringPattern; 36 import classycle.util.OrStringPattern; 37 import classycle.util.StringPattern; 38 import classycle.util.WildCardPattern; 39 40 45 public class DependencyDefinitionParser 46 { 47 public static final String INDEPENDENT_OF_KEY_WORD = "independentOf", 48 EXCLUDING_KEY_WORD = "excluding", 49 DIRECTLY_INDEPENDENT_OF_KEY_WORD 50 = "directlyIndependentOf", 51 CHECK_KEY_WORD = "check", 52 LAYER_KEY_WORD = "layer", 53 SHOW_KEY_WORD = "show", 54 SETS_KEY_WORD = "sets", 55 CLASS_CYCLES_KEY_WORD = "absenceOfClassCycles", 56 PACKAGE_CYCLES_KEY_WORD = "absenceOfPackageCycles", 57 IN_KEY_WORD = "in", 58 LAYERING_OF_KEY_WORD = "layeringOf", 59 STRICT_LAYERING_OF_KEY_WORD = "strictLayeringOf"; 60 private static final String [] INDEPENDENT 61 = new String [] {INDEPENDENT_OF_KEY_WORD, 62 DIRECTLY_INDEPENDENT_OF_KEY_WORD}; 63 private static final String [] EXCLUDING = new String [] {EXCLUDING_KEY_WORD}; 64 private static final String PROP_DEF_BEGIN = "{"; 65 private static final String PROP_BEGIN = "${"; 66 private static final String PROP_END = "}"; 67 68 private final DependencyProperties _properties; 69 private final ResultRenderer _renderer; 70 final SetDefinitionRepository _setDefinitions 71 = new SetDefinitionRepository(); 72 final LayerDefinitionRepository _layerDefinitions 73 = new LayerDefinitionRepository(); 74 private final ArrayList _statements = new ArrayList (); 75 76 public DependencyDefinitionParser(String dependencyDefinition, 77 DependencyProperties properties, 78 ResultRenderer renderer) 79 { 80 _properties = properties; 81 _renderer = renderer; 82 try 83 { 84 StringBuffer buffer = new StringBuffer (); 85 BufferedReader reader = new BufferedReader (new StringReader ( 86 dependencyDefinition)); 87 String line; 88 int lineNumber = 0; 89 int lineNumberOfCurrentLogicalLine = 1; 90 while ((line = reader.readLine()) != null) 91 { 92 lineNumber++; 93 line = line.trim(); 94 if (!line.startsWith("#")) 95 { 96 buffer.append(line); 97 if (line.endsWith("\\")) 98 { 99 buffer.deleteCharAt(buffer.length() - 1).append(' '); 100 } else 101 { 102 String logicalLine = replaceProperties(new String (buffer).trim(), 103 lineNumberOfCurrentLogicalLine); 104 if (logicalLine.length() > 0) 105 { 106 parseLine(logicalLine, lineNumberOfCurrentLogicalLine); 107 } 108 buffer.setLength(0); 109 lineNumberOfCurrentLogicalLine = lineNumber + 1; 110 } 111 } 112 } 113 } catch (IOException e) 114 { 115 throw new IllegalArgumentException (e.toString()); 116 } 117 } 118 119 private String replaceProperties(String line, int lineNumber) 120 { 121 StringBuffer buffer = new StringBuffer (); 122 for (int i = 0; i < line.length(); ) 123 { 124 int index = line.indexOf(PROP_BEGIN, i); 125 if (index >= 0) 126 { 127 buffer.append(line.substring(i, index)); 128 i = line.indexOf(PROP_END, index); 129 if (i < 0) 130 { 131 throwException("Missing '" + PROP_END + "'.", lineNumber, -1); 132 } 133 String name = line.substring(index + PROP_BEGIN.length(), i); 134 i += PROP_END.length(); 135 String property = _properties.getProperty(name); 136 if (property == null) 137 { 138 String message = "Undefines property " + line.substring(index, i); 139 throwException(message, lineNumber, -1); 140 } else 141 { 142 buffer.append(property); 143 } 144 } else 145 { 146 buffer.append(line.substring(i)); 147 i = line.length(); 148 } 149 } 150 return new String (buffer); 151 } 152 153 public Statement[] getStatements() 154 { 155 return (Statement[]) _statements.toArray(new Statement[0]); 156 } 157 158 private void parseLine(String line, int lineNumber) { 159 if (line.startsWith(PROP_DEF_BEGIN)) 160 { 161 parsePropertyDefinition(line, lineNumber); 162 return; 163 } 164 StringTokenizer tokenizer = new StringTokenizer (line); 165 String [] tokens = new String [tokenizer.countTokens()]; 166 for (int i = 0; i < tokens.length; i++) 167 { 168 tokens[i] = tokenizer.nextToken(); 169 } 170 String firstToken = tokens[0]; 171 if (firstToken.startsWith("[")) 172 { 173 parseSetDefinition(tokens, lineNumber); 174 } else if (firstToken.equals(SHOW_KEY_WORD)) 175 { 176 parseShowStatement(tokens, lineNumber); 177 } else if (firstToken.equals(LAYER_KEY_WORD)) 178 { 179 parseLayerDefinition(tokens, lineNumber); 180 181 } else if (firstToken.equals(CHECK_KEY_WORD)) 182 { 183 parseCheckStatement(tokens, lineNumber); 184 185 } else 186 { 187 throwException("Expecting either a property definition, a set name, '" 188 + SHOW_KEY_WORD + "', '" + LAYER_KEY_WORD + "', or '" 189 + CHECK_KEY_WORD + "'.", 190 lineNumber, 0); 191 } 192 } 193 194 private void parsePropertyDefinition(String line, int lineNumber) 195 { 196 int index = line.indexOf(PROP_END); 197 if (index < 0) 198 { 199 throwException("Missing '" + PROP_END + "' in property definition.", 200 lineNumber, -1); 201 } 202 String name = line.substring(PROP_DEF_BEGIN.length(), index); 203 String def = line.substring(index + PROP_END.length()).trim(); 204 if (def.startsWith("=") == false) 205 { 206 throwException("Missing '=' in propety definition.", lineNumber, -1); 207 } 208 _properties.setProperty(name, def.substring(1).trim()); 209 } 210 211 private void parseSetDefinition(String [] tokens, int lineNumber) 212 { 213 String setName = tokens[0]; 214 if (setName.endsWith("]") == false) 215 { 216 throwException("Set name has to end with ']'.", lineNumber, 0); 217 } 218 if (_setDefinitions.contains(setName)) 219 { 220 throwException("Set " + setName + " already defined.", lineNumber, 0); 221 } 222 checkForEqualCharacter(tokens, lineNumber, 1); 223 StringPattern[][] lists = getLists(tokens, lineNumber, EXCLUDING, 2); 224 if (lists[0].length == 0 && lists[1].length == 0) 225 { 226 throwException("Missing terms in set definition.", lineNumber, 2); 227 } 228 AndStringPattern definition = new AndStringPattern(); 229 if (lists[0].length > 0) 230 { 231 definition.appendPattern(createOrSequence(lists[0])); 232 } 233 if (lists[1].length > 0) 234 { 235 definition.appendPattern(new NotStringPattern(createOrSequence(lists[1]))); 236 } 237 _setDefinitions.put(setName, definition); 238 } 239 240 private void checkForEqualCharacter(String [] tokens, int lineNumber, 241 int index) 242 { 243 if (tokens.length < index + 1 || !tokens[index].equals("=")) 244 { 245 throwException("'=' missing.", lineNumber, index); 246 } 247 } 248 249 private StringPattern createOrSequence(StringPattern[] patterns) 250 { 251 OrStringPattern result = new OrStringPattern(); 252 for (int i = 0; i < patterns.length; i++) 253 { 254 result.appendPattern(patterns[i]); 255 } 256 return result; 257 } 258 259 private StringPattern createPattern(String term, int lineNumber, 260 int tokenIndex) 261 { 262 StringPattern pattern = _setDefinitions.getPattern(term); 263 if (pattern == null) 264 { 265 if (term.startsWith("[") && term.endsWith("]")) 266 { 267 throwException("Set " + term + " is undefined.", 268 lineNumber, tokenIndex); 269 } 270 if (term.indexOf('.') < 0 && term.indexOf('*') < 0 271 && term.length() > 0 && Character.isLowerCase(term.charAt(0))) 272 { 273 throwException("Patterns without a '.' and a '*' should not start " 274 + "with a lower-case letter: " + term, 275 lineNumber, tokenIndex); 276 } 277 pattern = new WildCardPattern(term); 278 } 279 return pattern; 280 } 281 282 private void parseLayerDefinition(String [] tokens, int lineNumber) 283 { 284 if (tokens.length < 2) 285 { 286 throwException("Missing layer name.", lineNumber, 1); 287 } 288 String layerName = tokens[1]; 289 if (_layerDefinitions.contains(layerName)) 290 { 291 throwException("Layer '" + layerName + "' already defined.", 292 lineNumber, 1); 293 } 294 checkForEqualCharacter(tokens, lineNumber, 2); 295 if (tokens.length < 4) 296 { 297 throwException("Missing terms in definition of layer '" 298 + layerName + "'.", lineNumber, 3); 299 } 300 ArrayList layer = new ArrayList (); 301 for (int i = 3; i < tokens.length; i++) 302 { 303 layer.add(createPattern(tokens[i], lineNumber, i)); 304 } 305 StringPattern[] sets = new StringPattern[layer.size()]; 306 _layerDefinitions.put(layerName, (StringPattern[]) layer.toArray(sets)); 307 } 308 309 private void parseShowStatement(String [] tokens, int lineNumber) 310 { 311 if (tokens.length < 2) { 312 throwException("Missing display preference(s).", lineNumber, 1); 313 } 314 Preference[] preferences = new Preference[tokens.length - 1]; 315 for (int i = 0; i < preferences.length; i++) 316 { 317 preferences[i] = _renderer.getPreferenceFactory().get(tokens[i + 1]); 318 if (preferences[i] == null) 319 { 320 throwException("Unknown display preference: " + tokens[i + 1], 321 lineNumber, i + 1); 322 } 323 } 324 _statements.add(new ShowStatement(_renderer, preferences)); 325 } 326 327 private void parseCheckStatement(String [] tokens, int lineNumber) 328 { 329 if (tokens.length < 2) 330 { 331 throwException("Missing checking statement.", lineNumber, 1); 332 } 333 if (tokens[1].equals(STRICT_LAYERING_OF_KEY_WORD) 334 || tokens[1].equals(LAYERING_OF_KEY_WORD)) 335 { 336 createLayeringStatement(tokens, lineNumber); 337 } else if (tokens[1].equals(SETS_KEY_WORD)) 338 { 339 createCheckSetStatements(tokens, lineNumber); 340 } else if (tokens[1].equals(CLASS_CYCLES_KEY_WORD) 341 || tokens[1].equals(PACKAGE_CYCLES_KEY_WORD)) 342 { 343 createCyclesStatement(tokens, lineNumber); 344 } else 345 { 346 createDependencyStatement(tokens, lineNumber); 347 } 348 } 349 350 private void createCyclesStatement(String [] tokens, int lineNumber) 351 { 352 boolean packageCycles = tokens[1].equals(PACKAGE_CYCLES_KEY_WORD); 353 if (tokens.length != 6) 354 { 355 throwException("Invalid statement.", lineNumber, tokens.length); 356 } 357 if (tokens[2].equals(">") == false) 358 { 359 throwException("'>' expected.", lineNumber, 2); 360 } 361 int size = 0; 362 try 363 { 364 size = Integer.parseInt(tokens[3]); 365 } catch (NumberFormatException e) 366 { 367 throwException("Number expected.", lineNumber, 3); 368 } 369 if (size < 1) 370 { 371 throwException("Size has to be >= 1", lineNumber, 3); 372 } 373 if (tokens[4].equals(IN_KEY_WORD) == false) 374 { 375 throwException("'in' expected.", lineNumber, 4); 376 } 377 StringPattern pattern = createPattern(tokens[5], lineNumber, 4); 378 _statements.add(new CheckCyclesStatement(pattern, size, packageCycles, 379 _setDefinitions)); 380 } 381 382 private void createCheckSetStatements(String [] tokens, int lineNumber) 383 { 384 if (tokens.length < 3) 385 { 386 throwException("No sets to check.", lineNumber, 2); 387 } 388 for (int i = 2; i < tokens.length; i++) 389 { 390 StringPattern pattern = createPattern(tokens[i], lineNumber, i); 391 _statements.add(new CheckSetStatement(pattern, _setDefinitions)); 392 } 393 } 394 395 private void createLayeringStatement(String [] tokens, int lineNumber) 396 { 397 StringPattern[][] layers = new StringPattern[tokens.length - 2][]; 398 for (int i = 0; i < layers.length; i++) 399 { 400 String name = tokens[i + 2]; 401 layers[i] = _layerDefinitions.getLayer(name); 402 if (layers[i] == null) 403 { 404 throwException("Undefined layer '" + name + "'.", lineNumber, i + 2); 405 } 406 } 407 boolean strict = tokens[1].equals(STRICT_LAYERING_OF_KEY_WORD); 408 _statements.add(new LayeringStatement(layers, strict, _setDefinitions, 409 _layerDefinitions, _renderer)); 410 } 411 412 private void createDependencyStatement(String [] tokens, int lineNumber) 413 { 414 StringPattern[][] lists = getLists(tokens, lineNumber, INDEPENDENT, 1); 415 if (lists[0].length == 0) 416 { 417 throwException("Missing start sets.", lineNumber, 1); 418 } 419 if (lists[1].length == 0) 420 { 421 throwException("Missing end sets. Probably one of the following " 422 + "key words are missing: " 423 + Arrays.asList(INDEPENDENT), lineNumber, tokens.length); 424 } 425 boolean directPathsOnly = DIRECTLY_INDEPENDENT_OF_KEY_WORD.equals( 426 tokens[lists[0].length + 1]); 427 _statements.add(new DependencyStatement(lists[0], lists[1], directPathsOnly, _setDefinitions, 428 _renderer)); 429 } 430 431 private StringPattern[][] getLists(String [] tokens, int lineNumber, 432 String [] keyWords, int startIndex) 433 { 434 ArrayList startSets = new ArrayList (); 435 ArrayList endSets = new ArrayList (); 436 ArrayList currentList = startSets; 437 for (int i = startIndex; i < tokens.length; i++) 438 { 439 String token = tokens[i]; 440 if (isAKeyWord(token, keyWords)) 441 { 442 if (currentList == endSets) 443 { 444 throwException("Invalid appearance of key word '" + token + "'.", 445 lineNumber, i); 446 } 447 currentList = endSets; 448 } else 449 { 450 currentList.add(createPattern(token, lineNumber, i)); 451 } 452 } 453 StringPattern[][] result = new StringPattern[2][]; 454 result[0] = (StringPattern[]) startSets.toArray(new StringPattern[0]); 455 result[1] = (StringPattern[]) endSets.toArray(new StringPattern[0]); 456 return result; 457 } 458 459 private boolean isAKeyWord(String token, String [] keyWords) 460 { 461 boolean result = false; 462 for (int i = 0; i < keyWords.length; i++) 463 { 464 if (keyWords[i].equals(token)) 465 { 466 result = true; 467 break; 468 } 469 } 470 return result; 471 } 472 473 private void throwException(String message, int lineNumber, 474 int tokenIndex) 475 { 476 StringBuffer buffer = new StringBuffer ("Error in line "); 477 buffer.append(lineNumber); 478 if (tokenIndex >= 0) 479 { 480 buffer.append(" token ").append(tokenIndex + 1); 481 } 482 buffer.append(": ").append(message); 483 throw new IllegalArgumentException (new String (buffer)); 484 } 485 } 486 | Popular Tags |