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

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