source: default/trunk/de.ugoe.cs.swe.bnftools.ebnf/src/de/ugoe/cs/swe/bnftools/validation/EbnfJavaValidator.java @ 49

Last change on this file since 49 was 49, checked in by zeiss, 13 years ago
  • Property svn:mime-type set to text/plain
File size: 17.5 KB
Line 
1package de.ugoe.cs.swe.bnftools.validation;
2
3import java.util.ArrayList;
4import java.util.List;
5import java.util.Stack;
6
7import org.eclipse.emf.common.util.EList;
8import org.eclipse.emf.common.util.URI;
9import org.eclipse.emf.ecore.EObject;
10import org.eclipse.emf.ecore.resource.Resource;
11import org.eclipse.xtext.parsetree.AbstractNode;
12import org.eclipse.xtext.parsetree.CompositeNode;
13import org.eclipse.xtext.parsetree.LeafNode;
14import org.eclipse.xtext.parsetree.NodeUtil;
15import org.eclipse.xtext.resource.IResourceDescriptions;
16import org.eclipse.xtext.resource.XtextResource;
17import org.eclipse.xtext.resource.XtextResourceSet;
18import org.eclipse.xtext.validation.Check;
19import org.eclipse.xtext.validation.CheckType;
20
21import com.google.inject.Inject;
22
23import de.ugoe.cs.swe.bnftools.analysis.EbnfAnalysisUtils;
24import de.ugoe.cs.swe.bnftools.ebnf.BnfEntry;
25import de.ugoe.cs.swe.bnftools.ebnf.DefinitionList;
26import de.ugoe.cs.swe.bnftools.ebnf.EtsiBnf;
27import de.ugoe.cs.swe.bnftools.ebnf.ExtRule;
28import de.ugoe.cs.swe.bnftools.ebnf.Import;
29import de.ugoe.cs.swe.bnftools.ebnf.Rule;
30import de.ugoe.cs.swe.bnftools.ebnf.SingleDefinition;
31
32public class EbnfJavaValidator extends AbstractEbnfJavaValidator {
33
34        @Inject
35        IResourceDescriptions resourceDescriptions;
36
37        public static final String ruleReferencedOneDescription = "The rule is only referenced by one other rule";
38        public static final String passthroughRuleDescription = "The rule is a passthrough rule";
39        public static final String unreferencedPassthroughRuleDescription = "The rule is an unreferenced passthrough rule";
40        public static final String unusedRuleDescription = "The rule is not referenced anywhere";
41        public static final String equalAlternativeDescription = "The rule contains equal alternatives";
42        public static final String duplicateRulesDescription = "The rule is a duplicate";
43        public static final String duplicateSubRulesDescription = "A part of rule is a duplicate";
44        public static final String packageConsistencyCheckRuleMissingDescription = "Rule has been removed or renamed in the updated grammar. The package rule is therefore now an additional rule instead of a replacement rule. The package rule might therefore be inconsistent with the updated core grammar.";
45        public static final String packageConsistencyCheckRuleDifferentDescription = "Corresponding core grammar rule has been changed in updated grammar. The package rule might have become inconsistent with the updated core grammar.";
46        public static final String packageConsistencyCheckCorrespondingRuleMissingDescription = "Corresponding rule is missing in core grammar. Rule has been manually altered in the delta grammar (delta grammar is outdated) or the /core and /update flags are somehow switched.";
47
48        public static boolean checkReferencedOnlyOnce = false;
49        public static boolean checkPassthroughRule = false;
50        public static boolean checkUnusedRule = false;
51        public static boolean checkEqualAlternative = false;
52        public static boolean checkDuplicateRules = false;
53        public static boolean checkSubruleDuplicates = false;
54        public static boolean checkUpdatedGrammarConsistency = false;
55
56        // ----------------------------------------------------------------------------------------------------
57
58        @Check(CheckType.EXPENSIVE)
59        public void checkUpdateGrammarConsistency(EtsiBnf bnf) {
60                if (!checkUpdatedGrammarConsistency)
61                        return;
62
63                CompositeNode compNode = NodeUtil.getRootNode(bnf);
64                XtextResourceSet set = new XtextResourceSet();
65                URI uri = compNode.getElement().eResource().getURI();
66                Iterable<AbstractNode> allNodes = NodeUtil.getAllContents(compNode);
67                if(!bnf.getType().equals("/delta"))
68                        return;
69               
70                Resource coreRes = null;
71                Resource updatedRes = null;
72                EList<Import> imports = bnf.getImportSection().getImports();
73
74                for(int j=0; j<imports.size(); j++) {
75                        if(imports.get(j).getGrammarType().equals("core")) {
76                                String packageUri = uri.trimSegments(1).toPlatformString(true);
77                                String fullUri = packageUri + "/" +imports.get(j).getImportURI();
78                                coreRes= set.getResource( URI.createPlatformResourceURI(fullUri, true), true);
79                        }
80                        if(imports.get(j).getGrammarType().equals("update")) {
81                                String packageUri = uri.trimSegments(1).toPlatformString(true);
82                                String fullUri = packageUri + "/" +imports.get(j).getImportURI();
83                                updatedRes= set.getResource( URI.createPlatformResourceURI(fullUri, true), true);
84                        }
85                }
86               
87                if( (coreRes==null) || (updatedRes==null))
88                        return;
89
90                XtextResource coreResource = null;
91                XtextResource updatedResource = null;
92
93                if(coreRes instanceof XtextResource)
94                        coreResource = (XtextResource) coreRes;
95                if(updatedRes instanceof XtextResource)
96                        updatedResource = (XtextResource) updatedRes;
97
98                /*
99                 * the idea: get the core grammar and the updated grammar
100                 * for each extension rule in the delta grammar,
101                 *              find the corresponding rule in the updated grammar and the core grammar
102                 *              if it doesn't exist in the updated grammar write the inconsistency
103                 *              if it exists, compare to the rule in the core grammar
104                 */
105                for(AbstractNode node: allNodes) {
106                        if(node.getElement() instanceof ExtRule) {
107                                checkForConsistency(node, coreResource, updatedResource, compNode);
108                        }
109                }
110        }
111       
112        private boolean checkForConsistency(AbstractNode node, XtextResource coreResource, XtextResource updatedResource, CompositeNode compNode) {
113                //updated grammar:
114                AbstractNode updatedRule = null;
115                AbstractNode coreRule = null;
116                Rule currentRule = null;
117                for(AbstractNode uNode : NodeUtil.getAllContents(updatedResource.getParseResult().getRootNode())) {
118                        if (uNode.getElement() instanceof Rule) {
119                                currentRule  = (Rule) uNode.getElement();
120                                if (currentRule.getName().equals(((ExtRule) node.getElement()).getName())) {
121                                        updatedRule = uNode;
122                                        break;
123                                }
124                        }
125                }
126               
127                if(updatedRule == null) {
128                        warning(EbnfJavaValidator.packageConsistencyCheckRuleMissingDescription, node.getElement(), 1);
129                        return false;
130                } else { //core grammar
131                        for(AbstractNode cNode : NodeUtil.getAllContents(coreResource.getParseResult().getRootNode())) {
132                                if(cNode.getElement() instanceof Rule)
133                                        if(((Rule) cNode.getElement()).getName().equals(((ExtRule) node.getElement()).getName())) {
134                                                coreRule = cNode;
135                                                break;
136                                        }
137                        }
138                       
139                        if(coreRule == null){
140                                warning(EbnfJavaValidator.packageConsistencyCheckCorrespondingRuleMissingDescription, node.getElement(), 1);
141                                return false;
142                        }
143                       
144                        if (compareRules(coreRule, updatedRule)) {
145                                return true;
146                        } else {
147                                warning(EbnfJavaValidator.packageConsistencyCheckRuleDifferentDescription, node.getElement(), 1);
148                                return false;
149                        }
150                       
151                }
152        }
153
154        // returns true, if the rules are equal
155        private boolean compareRules(AbstractNode cNode, AbstractNode uNode) {
156                EList<LeafNode> cLeaves = removeWS(cNode.getLeafNodes());
157                EList<LeafNode> uLeaves = removeWS(uNode.getLeafNodes());
158                if (cLeaves.size() != uLeaves.size())
159                        return false;
160               
161                for(int i=0; i < cLeaves.size(); i++) {
162                        if(!(cLeaves.get(i).serialize().equals(uLeaves.get(i).serialize())))
163                                return false;
164                       
165                }
166                return true;
167        }
168
169        private EList<LeafNode> removeWS(EList<LeafNode> leaves) {
170                for(int i=0; i < leaves.size(); i++) {
171                        if(!(leaves.get(i).getGrammarElement() instanceof org.eclipse.xtext.impl.TerminalRuleImpl))
172                                leaves.remove(i);
173                }
174                return leaves;
175        }
176       
177        // ----------------------------------------------------------------------------------------------------
178
179        @Check(CheckType.EXPENSIVE)
180        public void checkReferencedOnlyOnce(Rule rule) {
181                if (!checkReferencedOnlyOnce)
182                        return;
183
184                if (EbnfAnalysisUtils.isTokenRule(rule))
185                        return;
186
187                List<Rule> references = EbnfAnalysisUtils.findReferences(rule,
188                                resourceDescriptions);
189
190                if (references.size() == 1) {
191                        warning(EbnfJavaValidator.ruleReferencedOneDescription, 1,
192                                        EbnfJavaValidator.ruleReferencedOneDescription);
193                }
194        }
195
196        // ----------------------------------------------------------------------------------------------------
197
198        @Check(CheckType.EXPENSIVE)
199        public void checkPassthroughRule(Rule rule) {
200                if (!checkPassthroughRule)
201                        return;
202
203                List<Rule> references = EbnfAnalysisUtils.findReferences(rule,
204                                resourceDescriptions);
205
206                if (EbnfAnalysisUtils.isPassthroughRule(rule)) {
207                        if (references.size() == 0) {
208                                warning(
209                                                EbnfJavaValidator.unreferencedPassthroughRuleDescription,
210                                                1,
211                                                EbnfJavaValidator.unreferencedPassthroughRuleDescription);
212                        } else {
213                                warning(EbnfJavaValidator.passthroughRuleDescription, 1,
214                                                EbnfJavaValidator.passthroughRuleDescription);
215                        }
216                }
217        }
218
219        // ----------------------------------------------------------------------------------------------------
220
221        @Check(CheckType.EXPENSIVE)
222        public void checkUnusedRule(Rule rule) {
223                if (!checkUnusedRule)
224                        return;
225
226                List<Rule> references = EbnfAnalysisUtils.findReferences(rule,
227                                resourceDescriptions);
228
229                if ((references.size() == 0) && (rule.getRulenumber() != 1))
230                        warning(EbnfJavaValidator.unusedRuleDescription, 1,
231                                        EbnfJavaValidator.unusedRuleDescription);
232        }
233
234        // ----------------------------------------------------------------------------------------------------
235
236        private List<DefinitionList> collectDefinitionLists(CompositeNode o) {
237                List<DefinitionList> list = new ArrayList<DefinitionList>();
238
239                for (int i = 0; i < o.getChildren().size(); i++) {
240                        AbstractNode child = o.getChildren().get(i);
241                        if (child.getElement() instanceof DefinitionList) {
242                                list.add((DefinitionList) child.getElement());
243                        }
244
245                        if (child instanceof CompositeNode) {
246                                list.addAll(collectDefinitionLists((CompositeNode) child));
247                        }
248                }
249
250                return list;
251        }
252
253        // ----------------------------------------------------------------------------------------------------
254
255        @Check(CheckType.EXPENSIVE)
256        public void checkEqualAlternative(Rule rule) {
257                if (!checkEqualAlternative)
258                        return;
259
260                // TODO: is not per rule, but global!
261
262                // quadratic!
263                CompositeNode startNode = NodeUtil.getNodeAdapter(rule).getParserNode();
264
265                List<DefinitionList> definitionLists = collectDefinitionLists(startNode);
266
267                for (int k = 0; k < definitionLists.size(); k++) {
268
269                        EList<SingleDefinition> singleDefinitions = definitionLists.get(k)
270                                        .getSingleDefinition();
271                        for (int i = 0; i < singleDefinitions.size(); i++) {
272                                for (int j = 0; j < singleDefinitions.size(); j++) {
273                                        if (i == j)
274                                                continue;
275
276                                        SingleDefinition upper = singleDefinitions.get(i);
277                                        SingleDefinition lower = singleDefinitions.get(j);
278                                        CompositeNode upperNode = NodeUtil.getNodeAdapter(upper)
279                                                        .getParserNode();
280                                        CompositeNode lowerNode = NodeUtil.getNodeAdapter(lower)
281                                                        .getParserNode();
282
283                                        String upperString = upperNode.serialize().trim()
284                                                        .replaceAll("[ \t\n\r]", "");
285                                        String lowerString = lowerNode.serialize().trim()
286                                                        .replaceAll("[ \t\n\r]", "");
287
288                                        if (lowerString.equals(upperString))
289                                                warning(EbnfJavaValidator.equalAlternativeDescription,
290                                                                1,
291                                                                EbnfJavaValidator.equalAlternativeDescription);
292                                }
293                        }
294                }
295
296        }
297
298        // ----------------------------------------------------------------------------------------------------
299
300        @Check(CheckType.EXPENSIVE)
301        public void checkDuplicateRules(Rule rule) {
302                if (!checkDuplicateRules)
303                        return;
304
305                EtsiBnf etsiBnfNode = (EtsiBnf) rule.eContainer().eContainer();
306               
307                CompositeNode definitionList = NodeUtil.getNodeAdapter(rule.getDefinitionList()).getParserNode();
308               
309                String rightHandSideText = definitionList.serialize().trim()
310                                .replaceAll("[ \t\n\r]", "");
311
312                for (int i = 0; i < etsiBnfNode.getBnfEntry().size(); i++) {
313                        BnfEntry currentNode = etsiBnfNode.getBnfEntry().get(i);
314                        if (currentNode == null)
315                                continue;
316                        if (currentNode.getRule() == null)
317                                continue;
318                        if (currentNode.getRule() == rule)
319                                continue;
320
321                        Rule currentRule = currentNode.getRule();
322                        CompositeNode currentRuleDefinitionList = NodeUtil.getNodeAdapter(currentRule.getDefinitionList()).getParserNode();
323                        String currentRuleRightHandSideText = currentRuleDefinitionList.serialize().trim()
324                        .replaceAll("[ \t\n\r]", "");
325
326                        if (currentRuleRightHandSideText.equals(rightHandSideText)) {
327                                        String description = EbnfJavaValidator.duplicateRulesDescription
328                                                        + " with rule \""
329                                                        + currentRule.getName()
330                                                        + "\" (Line "
331                                                        + NodeUtil.getNodeAdapter(currentRule).getParserNode().getLine() + ")";
332                                        warning(description, 1, description);
333                                }
334                        }
335        }
336       
337        // ----------------------------------------------------------------------------------------------------
338       
339        @Check(CheckType.EXPENSIVE)
340        public void checkSubruleDuplicates(EtsiBnf bnf) {
341                if (!checkSubruleDuplicates)
342                        return;
343
344                for (int i=0; i < bnf.getBnfEntry().size(); i++) {
345                        BnfEntry currentBnfEntry = bnf.getBnfEntry().get(i);
346                        if ((currentBnfEntry.getRule() != null) && (currentBnfEntry.getRule().getDefinitionList() != null)) {
347                               
348                                CompositeNode ruleParserNode = NodeUtil.getNodeAdapter(currentBnfEntry.getRule().getDefinitionList()).getParserNode();
349                                String ruleText = ruleParserNode.serialize().trim();
350                                ruleText = ruleText + " ";
351                                String processedRuleText = ruleText.replaceAll("([^\"])\\(([^\"])", "$1 ( $2"); // ( case 1
352                                processedRuleText = processedRuleText.replaceAll("\\(([^\"])", " ( $1"); // ( case 2
353                                processedRuleText = processedRuleText.replaceAll("([^\"])\\(\"", "$1 ( \""); // ( case 3
354
355                                processedRuleText = processedRuleText.replaceAll("([^\"])\\)([^\"])", "$1 ) $2"); // ) case 1
356                                processedRuleText = processedRuleText.replaceAll("\\)([^\"])", " ) $1"); // ) case 2
357                                processedRuleText = processedRuleText.replaceAll("\"\\)([^\"])", "\" ) $1"); // ) case 3
358                               
359                                processedRuleText = processedRuleText.replaceAll("([^\"])\\{([^\"])", "$1 { $2"); // { case 1
360                                processedRuleText = processedRuleText.replaceAll("\\{([^\"])", " { $1"); // { case 2
361                                processedRuleText = processedRuleText.replaceAll("([^\"])\\{\"", "$1 { \""); // { case 3
362                               
363                                processedRuleText = processedRuleText.replaceAll("([^\"])\\}([^\\+\"])", "$1 } $2"); // } case 1
364                                processedRuleText = processedRuleText.replaceAll("\\}([^\\+\"])", " } $1"); // } case 2
365                                processedRuleText = processedRuleText.replaceAll("\"\\}([^\\+\"])", "\" } $1"); // } case 3
366                               
367                                processedRuleText = processedRuleText.replaceAll("([^\"])\\}\\+([^\"])", "$1 }\\+ $2"); // }+ case 1
368                                processedRuleText = processedRuleText.replaceAll("\\}\\+([^\"])", " }\\+ $1 "); // }+ case 2
369                                processedRuleText = processedRuleText.replaceAll("\"\\}\\+([^\"])", "\" }\\+ $1 "); // }+ case 3
370                               
371                                processedRuleText = processedRuleText.replaceAll("([^\"])\\[([^\"])", "$1 [ $2"); // [ case 1
372                                processedRuleText = processedRuleText.replaceAll("\\[([^\"])", " [ $1"); // [ case 2
373                                processedRuleText = processedRuleText.replaceAll("([^\"])\\[\"", "$1 [ \""); // [ case 3
374
375                                processedRuleText = processedRuleText.replaceAll("([^\"])\\]([^\"])", "$1 ] $2"); // ] case 1
376                                processedRuleText = processedRuleText.replaceAll("\\]([^\"])", " ] $1"); // ] case 2
377                                processedRuleText = processedRuleText.replaceAll("\"\\]([^\"])", "\" ] $1"); // ] case 3
378
379                               
380                                String trimmedRuleText = processedRuleText.replaceAll("[\t\n\r]", " ").replaceAll("[ ]+", " ");
381                                System.out.println("rule: " + trimmedRuleText);
382                               
383//                              System.out.println(trimmedRuleText);
384                                String[] parts = trimmedRuleText.split(" ");
385                               
386                                for (int j=0; j <= parts.length; j++) {
387                                        for (int k=j; k <= parts.length; k++) {
388                                                if (isBalancedPairs(parts, j, k) && (k-j > 1)) {
389                                                        String balancedSubString = createString(parts, j, k);
390                                                        System.out.println(balancedSubString);
391                                                }
392
393//                                              combined.append(parts[k]);
394//                                              combined.append(" ");
395                                        }
396                                }
397                               
398                                System.out.println("---------------------------------------------");
399
400                               
401                               
402                               
403                        }
404                }
405               
406               
407               
408               
409//              System.out.println("rule name: " + rule.getName());
410//              for (int i=0; i < definitionLists.size(); i++) {
411//                      DefinitionList currentDefList = definitionLists.get(i);
412//                     
413//                      CompositeNode currentRuleDefinitionList = NodeUtil.getNodeAdapter(currentDefList).getParserNode();
414//                      String currentRuleDefinitionListText = currentRuleDefinitionList.serialize().trim();
415//                      String trimmedCurrentRuleDefinitionListText = currentRuleDefinitionListText.replaceAll("[ \t\n\r]", "");
416//                      System.out.println(trimmedCurrentRuleDefinitionListText);
417////                    System.out.println("  -> " + currentRuleDefinitionListText);
418//
419//                     
420//              }
421               
422        }
423       
424        private boolean isBalancedPairs(String[] parts, int start, int end) {
425                Stack<String> pairStack = new Stack<String>();
426
427                if (start == end)
428                        return false;
429               
430                if (parts[start].contains("|"))
431                        return false;
432
433                if (parts[start].contains("}"))
434                        return false;
435
436                if (parts[start].contains("}+"))
437                        return false;
438               
439                if (parts[start].contains("]"))
440                        return false;
441
442                if (parts[end-1].contains("|"))
443                        return false;
444               
445                for (int i=start; i < end; i++) {
446                        String currentPart = parts[i];
447                        if (currentPart.equals("(") || currentPart.equals("{") || currentPart.equals("[")) {
448                                pairStack.push(currentPart);
449                        }
450                       
451                        if (!pairStack.isEmpty()) {
452                                if (currentPart.equals(")") && pairStack.peek().equals("(")) {
453                                        pairStack.pop();
454                                } else if (currentPart.equals("}") && pairStack.peek().equals("{")) {
455                                        pairStack.pop();
456                                } else if (currentPart.equals("}+") && pairStack.peek().equals("{")) {
457                                        pairStack.pop();
458                                } else if (currentPart.equals("]") && pairStack.peek().equals("[")) {
459                                        pairStack.pop();
460                                }
461                        } else {
462                                if (currentPart.equals(")") || currentPart.equals("}") || currentPart.equals("}+") || currentPart.equals("]")) {
463                                        return false;
464                                }
465                        }
466                }
467               
468                if (pairStack.empty())
469                        return true;
470               
471                return false;
472               
473        }
474       
475        private String createString(String[] parts, int start, int end) {
476                StringBuffer result = new StringBuffer();
477               
478                for (int i=start; i < end; i++) {
479                        result.append(parts[i]);
480                        result.append(" ");
481                }
482               
483                return result.toString().trim();
484        }
485       
486}
Note: See TracBrowser for help on using the repository browser.