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

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