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

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