KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > classycle > dependency > DependencyDefinitionParser


1 /*
2  * Copyright (c) 2003-2006, Franz-Josef Elmer, All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * - Redistributions of source code must retain the above copyright notice,
8  * this list of conditions and the following disclaimer.
9  * - Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
15  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */

25 package classycle.dependency;
26
27 import java.io.BufferedReader JavaDoc;
28 import java.io.IOException JavaDoc;
29 import java.io.StringReader JavaDoc;
30 import java.util.ArrayList JavaDoc;
31 import java.util.Arrays JavaDoc;
32 import java.util.StringTokenizer JavaDoc;
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 /**
41  * Parser for a dependency definition file.
42  *
43  * @author Franz-Josef Elmer
44  */

45 public class DependencyDefinitionParser
46 {
47   public static final String JavaDoc 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 JavaDoc[] INDEPENDENT
61       = new String JavaDoc[] {INDEPENDENT_OF_KEY_WORD,
62                       DIRECTLY_INDEPENDENT_OF_KEY_WORD};
63   private static final String JavaDoc[] EXCLUDING = new String JavaDoc[] {EXCLUDING_KEY_WORD};
64   private static final String JavaDoc PROP_DEF_BEGIN = "{";
65   private static final String JavaDoc PROP_BEGIN = "${";
66   private static final String JavaDoc 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 JavaDoc _statements = new ArrayList JavaDoc();
75   
76   public DependencyDefinitionParser(String JavaDoc dependencyDefinition,
77                                     DependencyProperties properties,
78                                     ResultRenderer renderer)
79   {
80     _properties = properties;
81     _renderer = renderer;
82     try
83     {
84       StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
85       BufferedReader JavaDoc reader = new BufferedReader JavaDoc(new StringReader JavaDoc(
86               dependencyDefinition));
87       String JavaDoc 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 JavaDoc logicalLine = replaceProperties(new String JavaDoc(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 JavaDoc e)
114     {
115       throw new IllegalArgumentException JavaDoc(e.toString());
116     }
117   }
118   
119   private String JavaDoc replaceProperties(String JavaDoc line, int lineNumber)
120   {
121     StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
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 JavaDoc name = line.substring(index + PROP_BEGIN.length(), i);
134         i += PROP_END.length();
135         String JavaDoc property = _properties.getProperty(name);
136         if (property == null)
137         {
138           String JavaDoc 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 JavaDoc(buffer);
151   }
152   
153   public Statement[] getStatements()
154   {
155     return (Statement[]) _statements.toArray(new Statement[0]);
156   }
157   
158   private void parseLine(String JavaDoc line, int lineNumber) {
159     if (line.startsWith(PROP_DEF_BEGIN))
160     {
161       parsePropertyDefinition(line, lineNumber);
162       return;
163     }
164     StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc(line);
165     String JavaDoc[] tokens = new String JavaDoc[tokenizer.countTokens()];
166     for (int i = 0; i < tokens.length; i++)
167     {
168       tokens[i] = tokenizer.nextToken();
169     }
170     String JavaDoc 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 JavaDoc 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 JavaDoc name = line.substring(PROP_DEF_BEGIN.length(), index);
203     String JavaDoc 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 JavaDoc[] tokens, int lineNumber)
212   {
213     String JavaDoc 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 JavaDoc[] 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 JavaDoc 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 JavaDoc[] tokens, int lineNumber)
283   {
284     if (tokens.length < 2)
285     {
286       throwException("Missing layer name.", lineNumber, 1);
287     }
288     String JavaDoc 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 JavaDoc layer = new ArrayList JavaDoc();
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 JavaDoc[] 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 JavaDoc[] 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 JavaDoc[] 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 JavaDoc 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 JavaDoc[] 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 JavaDoc[] tokens, int lineNumber)
396   {
397     StringPattern[][] layers = new StringPattern[tokens.length - 2][];
398     for (int i = 0; i < layers.length; i++)
399     {
400       String JavaDoc 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 JavaDoc[] 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 JavaDoc[] tokens, int lineNumber,
432                                      String JavaDoc[] keyWords, int startIndex)
433   {
434     ArrayList JavaDoc startSets = new ArrayList JavaDoc();
435     ArrayList JavaDoc endSets = new ArrayList JavaDoc();
436     ArrayList JavaDoc currentList = startSets;
437     for (int i = startIndex; i < tokens.length; i++)
438     {
439       String JavaDoc 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 JavaDoc token, String JavaDoc[] 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 JavaDoc message, int lineNumber,
474                               int tokenIndex)
475   {
476     StringBuffer JavaDoc buffer = new StringBuffer JavaDoc("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 JavaDoc(new String JavaDoc(buffer));
484   }
485 }
486
Popular Tags