source: default/trunk/de.ugoe.cs.swe.bnftools.ebnf.ui/src/de/ugoe/cs/swe/bnftools/ui/formatter/EbnfFormatterVisitor.java @ 41

Last change on this file since 41 was 41, checked in by zeiss, 14 years ago
  • Property svn:mime-type set to text/plain
File size: 17.2 KB
Line 
1package de.ugoe.cs.swe.bnftools.ui.formatter;
2
3import java.util.ArrayList;
4import java.util.Stack;
5
6import org.eclipse.emf.ecore.EObject;
7import org.eclipse.xtext.parsetree.AbstractNode;
8import org.eclipse.xtext.parsetree.CompositeNode;
9import org.eclipse.xtext.parsetree.LeafNode;
10import org.eclipse.xtext.parsetree.NodeUtil;
11
12import de.ugoe.cs.swe.bnftools.ebnf.Atom;
13import de.ugoe.cs.swe.bnftools.ebnf.BnfEntry;
14import de.ugoe.cs.swe.bnftools.ebnf.DefinitionList;
15import de.ugoe.cs.swe.bnftools.ebnf.DeltaEntry;
16import de.ugoe.cs.swe.bnftools.ebnf.EtsiBnf;
17import de.ugoe.cs.swe.bnftools.ebnf.ExtRule;
18import de.ugoe.cs.swe.bnftools.ebnf.GlobalCombinator;
19import de.ugoe.cs.swe.bnftools.ebnf.GroupedSequence;
20import de.ugoe.cs.swe.bnftools.ebnf.HookCombinator;
21import de.ugoe.cs.swe.bnftools.ebnf.Import;
22import de.ugoe.cs.swe.bnftools.ebnf.ImportSection;
23import de.ugoe.cs.swe.bnftools.ebnf.MergeEntry;
24import de.ugoe.cs.swe.bnftools.ebnf.MergeRule;
25import de.ugoe.cs.swe.bnftools.ebnf.OptionalSequence;
26import de.ugoe.cs.swe.bnftools.ebnf.RepeatedSequence;
27import de.ugoe.cs.swe.bnftools.ebnf.Rule;
28import de.ugoe.cs.swe.bnftools.ebnf.RuleCombinator;
29import de.ugoe.cs.swe.bnftools.ebnf.RuleReference;
30import de.ugoe.cs.swe.bnftools.ebnf.SectionHeading;
31import de.ugoe.cs.swe.bnftools.ebnf.SingleDefinition;
32import de.ugoe.cs.swe.bnftools.ebnf.StringRule;
33import de.ugoe.cs.swe.bnftools.ebnf.Term;
34import de.ugoe.cs.swe.bnftools.visitor.EbnfVisitor;
35
36public class EbnfFormatterVisitor extends EbnfVisitor {
37        protected StringBuffer buf;
38        protected FormatterConfig config;
39        protected int bufferPositionFormattedTextNoWhitespaces = 0;
40        protected int bufferPositionOriginalText = 0;
41        protected int allCommentsPosition = 0;
42        protected ArrayList<LeafNode> allComments = new ArrayList<LeafNode>();
43       
44        protected boolean lastWasSectionHeading=false;
45        protected CompositeNode parserEtsiBnfNode;
46        protected String originalText;
47        protected int bufferPositionFormattedText;
48        protected String formattedText;
49        protected String formattedTextNoWhitespaces;
50        protected String originalTextNoWhitespaces;
51        protected int newLineOffsetCounter = 0;
52        protected int rightHandSideRuleOffset = 0;
53        protected Stack<Integer> ruleSpacingStack = new Stack<Integer>();
54        protected Double averageSingleDefinitionLength;
55       
56        public EbnfFormatterVisitor(EObject rootNode, FormatterConfig config) {
57                super(rootNode);
58                this.config = config;
59                buf = new StringBuffer();
60        }
61
62        public EbnfFormatterVisitor(FormatterConfig config) {
63                this.config = config;
64                buf = new StringBuffer();
65        }
66
67        public StringBuffer getBuf() {
68                return buf;
69        }
70
71        protected boolean isCommentNode(LeafNode node) {
72                if ((node.getText().trim().startsWith("//") || node.getText().trim().startsWith("/*")) && (node.isHidden()))
73                        return true;
74                return false;
75        }
76       
77        protected void collectAllComments(CompositeNode node) {
78                for (int i=0; i < node.getChildren().size(); i++) {
79                        AbstractNode currentNode = node.getChildren().get(i);
80                        if (currentNode instanceof LeafNode) {
81                                LeafNode leafNode = (LeafNode) currentNode;
82                                if (isCommentNode(leafNode)) {
83                                        allComments.add(leafNode);
84                                }
85                        }
86                       
87                        if (currentNode instanceof CompositeNode) {
88                                collectAllComments((CompositeNode) currentNode);
89                        }
90                }
91        }
92
93        protected boolean isSingleLineComment(String str) {
94                if (str.startsWith("//"))
95                        return true;
96                return false;
97        }
98       
99        protected boolean isMultiLineComment(String str) {
100                if (str.startsWith("/*"))
101                        return true;
102                return false;
103        }
104
105        protected boolean isWhitespace(char ch) {
106                if ((ch==' ') || (ch == '\t') || (ch == '\n') || (ch == '\r'))
107                        return true;
108                return false;
109        }
110       
111        protected void skipWhitespacesOriginalText() {
112                while (bufferPositionOriginalText < originalText.length() && isWhitespace(originalText.charAt(bufferPositionOriginalText))) {
113                        bufferPositionOriginalText++;
114                }
115        }
116
117        protected void skipWhitespacesFormattedText(StringBuffer result) {
118                while (bufferPositionFormattedText < formattedText.length() && isWhitespace(formattedText.charAt(bufferPositionFormattedText))) {
119                        result.append(formattedText.substring(bufferPositionFormattedText, bufferPositionFormattedText+1));
120                        bufferPositionFormattedText++;
121                }
122        }
123
124        protected boolean isSingleLineCommentNext(String str, int position) {
125                if ((str.charAt(position) == '/') && (str.charAt(position) == '/'))
126                        return true;
127                return false;
128        }
129       
130        protected boolean isMultiLineCommentNext(String str, int position) {
131                if ((str.charAt(position) == '/') && (str.charAt(position) == '*'))
132                        return true;
133                return false;
134        }
135               
136        protected boolean isCommentNext(String str, int position) {
137                if (isSingleLineCommentNext(str, position) || isMultiLineCommentNext(str, position))
138                        return true;
139                else
140                        return false;
141        }
142       
143        protected String scanBackWhitespaces(String str, int position) {
144                StringBuffer whiteSpaces = new StringBuffer();
145                int currentPosition = position;
146                if (currentPosition >= str.length() || currentPosition < 0)
147                        return "";
148                char ch;
149                ch = str.charAt(currentPosition);
150                while (isWhitespace(ch)) {
151                        whiteSpaces.append(str.charAt(currentPosition));
152                        currentPosition--;
153                        if (currentPosition < 0)
154                                break;
155                        ch = str.charAt(currentPosition);
156                }
157                return whiteSpaces.toString();
158        }
159       
160        protected String stripEndingNewline(String str) {
161                int position = str.length() - 1;
162                while ((str.charAt(position) == '\n') || (str.charAt(position) == '\r')) {
163                        position--;
164                }
165                return str.substring(0, position + 1);
166        }
167       
168        protected int scanBackNewlinesCount(String str, int position) {
169                int newLinesCount = 0;
170                int currentPosition = position;
171                while ((str.charAt(currentPosition) == '\n') || (str.charAt(currentPosition) == '\r')) {
172                        if (str.charAt(currentPosition) == '\n') {
173                                if (str.charAt(currentPosition - 1) == '\r') {
174                                        currentPosition -= 2;
175                                } else {
176                                        currentPosition -= 1;
177                                }
178                                newLinesCount++;
179                        } else if (str.charAt(currentPosition) == '\r') {
180                                currentPosition -= 1;
181                                newLinesCount++;
182                        }
183                }
184               
185                return newLinesCount;
186        }
187       
188        protected void weaveComments() {
189                bufferPositionOriginalText = 0;
190                bufferPositionFormattedTextNoWhitespaces = 0;
191                bufferPositionFormattedText = 0;
192               
193                StringBuffer result = new StringBuffer();
194                formattedTextNoWhitespaces = buf.toString().replaceAll("[ \t\n\r]", "");
195                formattedText = buf.toString();
196               
197                while (bufferPositionFormattedTextNoWhitespaces <= formattedTextNoWhitespaces.length()) {
198                        skipWhitespacesOriginalText();
199                        skipWhitespacesFormattedText(result);
200                       
201                        if (!(bufferPositionOriginalText < originalText.length()))
202                                break;
203                       
204                        char formattedPositionNoWhitespaces;
205                        if (bufferPositionFormattedTextNoWhitespaces == formattedTextNoWhitespaces.length()) {
206                                formattedPositionNoWhitespaces = ' ';
207                        } else {
208                                formattedPositionNoWhitespaces = formattedTextNoWhitespaces.charAt(bufferPositionFormattedTextNoWhitespaces);
209                        }
210                        char originalPosition = originalText.charAt(bufferPositionOriginalText);
211
212                        if (formattedPositionNoWhitespaces != originalPosition) {
213                                if (formattedPositionNoWhitespaces == ';') { // formatted text always outputs the optional semicolon, skip it if necessary
214                                        bufferPositionFormattedTextNoWhitespaces++;
215                                        bufferPositionFormattedText++;
216                                } else if (isCommentNext(originalText, bufferPositionOriginalText)) {
217                                        LeafNode currentComment = allComments.get(allCommentsPosition);
218                                        if (currentComment.getTotalOffset() == bufferPositionOriginalText) {
219                                                if (isMultiLineComment(currentComment.getText())) {
220                                                        int newLinesCount = scanBackNewlinesCount(originalText, bufferPositionOriginalText-1);
221                                                        if (newLinesCount > 0) {
222                                                                if (scanBackNewlinesCount(result.toString(), result.toString().length()-1) == 0) {
223                                                                        result.append("\n\n");
224                                                                }
225                                                               
226                                                                result.append(currentComment.getText());
227                                                                result.append("\n");
228                                                        } else {
229                                                                String lastWhiteSpaces = scanBackWhitespaces(result.toString(), result.toString().length()-1);
230                                                                result.delete(result.toString().length() - lastWhiteSpaces.length(), result.toString().length());
231                                                                result.append(" " + stripEndingNewline(currentComment.getText()));
232                                                                result.append(lastWhiteSpaces);
233                                                        }
234                                                } else if (isSingleLineComment(currentComment.getText())) {
235                                                        int newLinesCount = scanBackNewlinesCount(originalText, bufferPositionOriginalText-1);
236                                                        String lastWhiteSpaces = scanBackWhitespaces(result.toString(), result.toString().length()-1);
237                                                        result.delete(result.toString().length() - lastWhiteSpaces.length(), result.toString().length());
238                                                        if (newLinesCount > 0) {
239                                                                result.append("\n\n" + stripEndingNewline(currentComment.getText()));
240                                                        } else {
241                                                                result.append(" " + stripEndingNewline(currentComment.getText()));
242                                                        }
243                                                        result.append(lastWhiteSpaces);
244                                                }
245                                                bufferPositionOriginalText+=currentComment.getLength();
246                                                allCommentsPosition++;
247                                        }
248                                } else { // disaster handling: return original unformatted text!
249                                        System.err.println("Disaster Recovery: returning original text!!");
250                                        buf = new StringBuffer();
251                                        buf.append(originalText);
252                                        return;
253                                }
254                        } else {
255                                result.append(formattedText.substring(bufferPositionFormattedText, bufferPositionFormattedText+1));
256                                bufferPositionOriginalText++;
257                                bufferPositionFormattedText++;
258                                bufferPositionFormattedTextNoWhitespaces++;
259                        }
260                }
261                buf = result;
262        }
263
264        protected void newLine() {
265                buf.append("\n");
266                if ((ruleSpacingStack != null) && (!ruleSpacingStack.empty())) {
267                        newLineOffsetCounter = ruleSpacingStack.peek();
268                } else {
269                        newLineOffsetCounter = 0;
270                }
271        }
272       
273        protected void text(String str) {
274                buf.append(str);
275                newLineOffsetCounter += str.length();
276        }
277
278        protected void space() {
279                buf.append(" ");
280                newLineOffsetCounter++;
281        }
282       
283        protected void spaces(int count) {
284                for (int i=0; i < count; i++) {
285                        buf.append(" ");
286                }
287        }
288
289        protected boolean lastIsClosingParentheses() {
290                char ch = buf.toString().charAt(buf.toString().length()-1);
291                if ((ch == ')') || (ch == ']') || (ch == '}'))
292                        return true;
293                return false;
294        }
295
296        protected void wrap() {
297                if ((config.isWrapAfterThreshold()) && (newLineOffsetCounter > config.getWrapThreshold())) {
298                        char last = buf.toString().charAt(buf.toString().length()-1);
299                        if (!((last == '(' || last == '[' || last == '{' ))) {
300                                newLine();
301                                if (ruleSpacingStack.size() > 1)
302                                        spaces(ruleSpacingStack.peek() + 1);
303                                else
304                                        spaces(ruleSpacingStack.peek());
305                        }
306                }
307        }
308
309        // -----------------------------------------------------------------------------
310
311        protected void visitBefore(EtsiBnf node) {
312                parserEtsiBnfNode = NodeUtil.getNodeAdapter(node).getParserNode();
313                collectAllComments(parserEtsiBnfNode);
314                originalText = NodeUtil.getNodeAdapter(node).getParserNode().serialize();
315                originalTextNoWhitespaces = originalText.replaceAll("[ \t\n\r]", "");
316               
317                text("grammar " + node.getName());
318                if (node.getType() != null)
319                        text(node.getType());
320                text(";");
321
322                newLine();
323                newLine();
324        }
325
326        protected void visitAfter(EtsiBnf node) {
327                weaveComments();
328        }
329
330        protected void visitBefore(ImportSection node) {
331        }
332
333        protected void visitAfter(ImportSection node) {
334                newLine();
335        }
336
337        protected void visitBefore(BnfEntry node) {
338        }
339
340        protected void visitAfter(BnfEntry node) {
341        }
342       
343        protected void visitBefore(DeltaEntry node) {
344        }
345
346        protected void visitAfter(DeltaEntry node) {
347        }
348       
349        protected void visitBefore(MergeEntry node) {
350        }
351
352        protected void visitAfter(MergeEntry node) {
353        }
354       
355        protected void visitBefore(Atom node) {
356        }
357
358        protected void visitAfter(Atom node) {
359        }
360
361        protected void visitBefore(Term node) {
362        }
363
364        protected void visitAfter(Term node) {
365                if (!isLastElement())
366                        space();
367        }
368
369        protected void visitBefore(DefinitionList node) {
370                averageSingleDefinitionLength = null;
371                int totalLength = 0;
372                for (int i=0; i < node.eContents().size(); i++) {
373                        CompositeNode parseNode = NodeUtil.getNodeAdapter(node.eContents().get(i)).getParserNode();
374                        totalLength += parseNode.serialize().trim().length();
375                }
376                averageSingleDefinitionLength = (double) totalLength / (double) node.eContents().size();
377        }
378
379        protected void visitAfter(DefinitionList node) {
380        }
381
382        protected void visitBefore(ExtRule node) {
383        }
384
385        protected void visitAfter(ExtRule node) {
386        }
387
388        protected void visitBefore(GlobalCombinator node) {
389        }
390
391        protected void visitAfter(GlobalCombinator node) {
392        }
393
394        protected void visitBefore(HookCombinator node) {
395        }
396
397        protected void visitAfter(HookCombinator node) {
398        }
399
400        protected void visitBefore(Import node) {
401                text("import \"" + node.getImportURI() + "\"");
402                if (node.getGrammarType() != null) {
403                        text("/" + node.getGrammarType());
404                }
405                if (node.getLabel() != null) {
406                        space();
407                        text("label: " + node.getLabel());
408                }
409                text(";");
410                newLine();
411        }
412
413        protected void visitAfter(Import node) {
414        }
415
416        protected void visitBefore(MergeRule node) {
417        }
418
419        protected void visitAfter(MergeRule node) {
420        }
421
422        protected void visitBefore(GroupedSequence node) {
423                wrap();
424                text("(");
425                ruleSpacingStack.push(newLineOffsetCounter-1);
426        }
427
428        protected void visitAfter(GroupedSequence node) {
429//              if ((config.isAlignParentheses() && (node.eContents().get(0).eContents().size() >= config.getAlignParenthesesElementCountThreshold())) || (lastIsClosingParentheses())) {
430                if (config.isAlignParentheses() && (node.eContents().get(0).eContents().size() >= config.getAlignParenthesesElementCountThreshold())) {
431                        newLine();
432                        spaces(ruleSpacingStack.peek());
433                }
434               
435                text(")");
436                ruleSpacingStack.pop();
437        }
438
439        protected void visitBefore(OptionalSequence node) {
440                wrap();
441                text("[");
442                ruleSpacingStack.push(newLineOffsetCounter-1);
443        }
444
445        protected void visitAfter(OptionalSequence node) {
446//              if ((config.isAlignParentheses() && (node.eContents().get(0).eContents().size() >= config.getAlignParenthesesElementCountThreshold())) || (lastIsClosingParentheses())) {
447                if (config.isAlignParentheses() && (node.eContents().get(0).eContents().size() >= config.getAlignParenthesesElementCountThreshold())) {
448                        newLine();
449                        spaces(ruleSpacingStack.peek());
450                }
451
452                text("]");
453                ruleSpacingStack.pop();
454        }
455
456        protected void visitBefore(RepeatedSequence node) {
457                wrap();
458                text("{");
459                ruleSpacingStack.push(newLineOffsetCounter-1);
460        }
461
462        protected void visitAfter(RepeatedSequence node) {
463//              if ((config.isAlignParentheses() && (node.eContents().get(0).eContents().size() >= config.getAlignParenthesesElementCountThreshold())) || (lastIsClosingParentheses())) {
464                if (config.isAlignParentheses() && (node.eContents().get(0).eContents().size() >= config.getAlignParenthesesElementCountThreshold())) {
465                        newLine();
466                        spaces(ruleSpacingStack.peek());
467                }
468
469                text("}");
470                if (node.isMorethanonce())
471                        text("+");
472                ruleSpacingStack.pop();
473        }
474
475        protected void visitBefore(Rule node) {
476                if (lastWasSectionHeading)
477                        newLine();
478               
479                lastWasSectionHeading=false;
480
481                newLineOffsetCounter = 0;
482
483                if (node.getRulenumber() > 0)
484                        text(node.getRulenumber() + ". ");
485               
486                text(node.getName() + " ::= ");
487               
488                rightHandSideRuleOffset = newLineOffsetCounter;
489                ruleSpacingStack.push(newLineOffsetCounter);
490        }
491
492        protected void visitAfter(Rule node) {
493                text(";");
494                newLine();
495                ruleSpacingStack.pop();
496        }
497
498        protected void visitBefore(RuleCombinator node) {
499        }
500
501        protected void visitAfter(RuleCombinator node) {
502        }
503
504        protected void visitBefore(RuleReference node) {
505                wrap();
506                text(node.getRuleref().getName());
507        }
508
509        protected void visitAfter(RuleReference node) {
510        }
511
512        protected void visitBefore(SectionHeading node) {
513                if (!lastWasSectionHeading && !buf.substring(buf.length()-2).equals("\n\n"))
514                        newLine();
515               
516                lastWasSectionHeading=true;
517               
518                text(node.getSectionHeader());
519        }
520
521        protected void visitAfter(SectionHeading node) {
522        }
523
524        protected void visitBefore(SingleDefinition node) {
525        }
526
527        protected void visitAfter(SingleDefinition node) {
528                boolean preventAlternativeBreakShortAlternatives = config.isPreventNewLineAfterAlternativeOnShortAlternatives() && (averageSingleDefinitionLength <= config.getShortAlternativeThreshold());
529                if (!isLastElement()) {
530                        text(" | ");
531                        if (config.isNewLineAfterAlternative()) {
532                                if (config.isPreventNewLineAfterAlternativeOnLessThanThreeElements()) {
533                                        DefinitionList definitionList = (DefinitionList) node.eContainer();
534                                        if ((definitionList.eContents().size() > 2) && (!preventAlternativeBreakShortAlternatives)) {
535                                                newLine();
536                                                if (ruleSpacingStack.size() > 1)
537                                                        spaces(ruleSpacingStack.peek() + 1);
538                                                else
539                                                        spaces(ruleSpacingStack.peek());
540                                        }
541                                } else {
542                                        if (!preventAlternativeBreakShortAlternatives) {
543                                                newLine();
544                                                if (ruleSpacingStack.size() > 1)
545                                                        spaces(ruleSpacingStack.peek() + 1);
546                                                else
547                                                        spaces(ruleSpacingStack.peek());
548                                        }
549                                }
550                        }
551                }
552        }
553
554        protected void visitBefore(StringRule node) {
555                wrap();
556                if (node.getLiteral() != null)
557                        text("\"" + node.getLiteral() + "\"");
558                else if (node.getColon() != null)
559                        text("\"\"\"");
560        }
561
562        protected void visitAfter(StringRule node) {
563        }
564       
565}
Note: See TracBrowser for help on using the repository browser.