View Javadoc
1   package net.sourceforge.jenesis4java.impl;
2   
3   /*
4    * #%L
5    * Jenesis 4 Java Code Generator
6    * %%
7    * Copyright (C) 2000 - 2015 jenesis4java
8    * %%
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation, either version 3 of the
12   * License, or (at your option) any later version.
13   * 
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Lesser Public License for more details.
18   * 
19   * You should have received a copy of the GNU General Lesser Public
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22   * #L%
23   */
24  
25  /**
26   * Copyright (C) 2008, 2010 Richard van Nieuwenhoven - ritchie [at] gmx [dot] at
27   * Copyright (C) 2000, 2001 Paul Cody Johnston - pcj@inxar.org <br>
28   * This file is part of Jenesis4java. Jenesis4java is free software: you can
29   * redistribute it and/or modify it under the terms of the GNU Lesser General
30   * Public License as published by the Free Software Foundation, either version 3
31   * of the License, or (at your option) any later version.<br>
32   * Jenesis4java is distributed in the hope that it will be useful, but WITHOUT
33   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
34   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
35   * details.<br>
36   * You should have received a copy of the GNU Lesser General Public License
37   * along with Jenesis4java. If not, see <http://www.gnu.org/licenses/>.
38   */
39  import java.io.PrintWriter;
40  import java.util.ArrayList;
41  import java.util.List;
42  
43  import net.sourceforge.jenesis4java.CodeWriter;
44  import net.sourceforge.jenesis4java.Codeable;
45  import net.sourceforge.jenesis4java.Comment;
46  import net.sourceforge.jenesis4java.CompilationUnit;
47  
48  /**
49   * Standard {@code CodeWriter} implementation.
50   */
51  public class MCodeWriter implements CodeWriter {
52  
53      // the actual writer
54      private final PrintWriter out;
55  
56      private int colNo = 0;
57  
58      private int lineNo = 1; // start at line 1
59  
60      private boolean isLineNew = true;
61  
62      /**
63       * Current depth of indentation.
64       */
65      private int indentDepth = 0;
66  
67      /**
68       * Indentation still to be applied. This is done lazy as soon as something is actually written.
69       * This is necessary to deal with the double-dedent problem, which would otherwise result
70       * in an undesirable new line.
71       */
72      private int indentationQueue = 0;
73  
74      private final List<Comment> queue = new ArrayList<>();
75  
76      private CompilationUnit compilationUnit;
77  
78      public MCodeWriter(PrintWriter out) {
79          this.out = out;
80      }
81  
82      /**
83       * Decrements the tab and makes sure, a new line is started.
84       */
85      @Override
86      public CodeWriter dedentLine() {
87          flushQueue();
88          indentDepth--;
89          ensureNewLine();
90          indentationQueue = indentDepth;
91          return this;
92      }
93  
94      /**
95       * Returns the current number of characters in the current line. It does not
96       * take the indent into account. Therefore, only the write methods and the
97       * space method increment the column counter.
98       */
99      @Override
100     public int getColumnNumber() {
101         return colNo;
102     }
103 
104     @Override
105     public CompilationUnit getCompilationUnit() {
106         return compilationUnit;
107     }
108 
109     /**
110      * Returns the current number of indentation levels.
111      */
112     @Override
113     public int getIndentNumber() {
114         return indentDepth;
115     }
116 
117     /**
118      * Returns the number of lines of the current document.
119      */
120     @Override
121     public int getLineNumber() {
122         return lineNo;
123     }
124 
125     /**
126      * Increments the tab and ensures an empty line.
127      */
128     @Override
129     public CodeWriter indentLine() {
130         flushQueue();
131         indentDepth++;
132         ensureNewLine();
133         indentationQueue = indentDepth;
134         return this;
135     }
136 
137     /**
138      * Returns true if no characters have been written since the last call of
139      * newLine().
140      */
141     @Override
142     public boolean isLineNew() {
143         return isLineNew;
144     }
145 
146     @Override
147     public CodeWriter ensureNewLine() {
148         if (!isLineNew()) {
149             newLine();
150         }
151         return this;
152     }
153 
154     /**
155      * Adds a the newLine string according to
156      * <CODE>System.getProperty("line.separator")</CODE> and the line is padded
157      * with the n tab characters where n is the number returned by
158      * {@code getIndentNumber()}.
159      */
160     @Override
161     public CodeWriter newLine() {
162         flushQueue();
163         out.println();
164         indentationQueue = indentDepth;
165         colNo = 0;
166         lineNo++;
167         isLineNew = true;
168         return this;
169     }
170 
171     /**
172      * This method allows those codeable objects to inject a comment without
173      * interrupting the line-by-line code itself. For example, if an expression
174      * wants to express a comment, it cannot do it until the end of the line.
175      * This method accepts a string argument. Before the newline is called, all
176      * comments given to the code writer will be written after newLine has been
177      * called.
178      */
179     @Override
180     public MCodeWriter queue(Comment comment) {
181         if (comment != null) {
182             comment.setText(comment.getText());
183             queue.add(comment);
184         }
185         return this;
186     }
187 
188     /**
189      * Resets the tab counter to zero and calls the newLine() method.
190      */
191     @Override
192     public CodeWriter resetLine() {
193         indentDepth = 0;
194         indentationQueue = 0;
195         newLine();
196         return this;
197     }
198 
199     @Override
200     public void setCompilationUnit(CompilationUnit compilationUnit) {
201         this.compilationUnit = compilationUnit;
202     }
203 
204     /**
205      * Writes a single space.
206      */
207     @Override
208     public CodeWriter space() {
209         writeIndent();
210         out.print(' ');
211         colNo++;
212         isLineNew = false;
213         return this;
214     }
215 
216     /**
217      * Writes a boolean.
218      */
219     @Override
220     public CodeWriter write(boolean b) {
221         writeIndent();
222         // print it
223         out.print(b);
224         // add if 4:'true' or 5:'false'
225         colNo += b ? 4 : 5;
226         isLineNew = false;
227         return this;
228     }
229 
230     /**
231      * Writes a single character.
232      */
233     @Override
234     public CodeWriter write(char c) {
235         writeIndent();
236         // print the char
237         out.print(c);
238         // add one
239         colNo++;
240         isLineNew = false;
241         return this;
242     }
243 
244     /**
245      * Writes an array of characters.
246      */
247     @Override
248     public CodeWriter write(char[] chars) {
249         if (chars != null) {
250             writeIndent();
251             // print the chars;
252             out.print(chars);
253             // add
254             colNo += chars.length;
255             isLineNew = false;
256         }
257         return this;
258     }
259 
260     /**
261      * Writes an array of characters.
262      */
263     @Override
264     public CodeWriter write(char[] chars, int off, int len) {
265         if (chars != null) {
266             writeIndent();
267             // print the chars;
268             out.write(chars, off, len);
269             // add
270             colNo += chars.length;
271             isLineNew = false;
272         }
273         return this;
274     }
275 
276     /**
277      * Instead of calling the {@code Object.toString()} method, the
278      * {@code Object.toCode(public CodeWriter)} method is invoked with
279      * {@code this} as the argument.
280      */
281     @Override
282     public CodeWriter write(Codeable ico) {
283         if (ico != null) {
284             writeIndent();
285             ico.toCode(this);
286         }
287         return this;
288     }
289 
290     /**
291      * Iterates the array and sends each element to {@code write(Codeable)} .
292      */
293     @Override
294     public CodeWriter write(Codeable[] aico) {
295         if (aico != null) {
296             writeIndent();
297             for (Codeable element : aico) {
298                 write(element);
299             }
300         }
301         return this;
302     }
303 
304     /**
305      * Writes a double.
306      */
307     @Override
308     public CodeWriter write(double d) {
309         writeIndent();
310         out.print(d);
311         colNo += Double.toString(d).length();
312         isLineNew = false;
313         return this;
314     }
315 
316     /**
317      * Writes a float.
318      */
319     @Override
320     public CodeWriter write(float f) {
321         writeIndent();
322         out.print(f);
323         colNo += Float.toString(f).length();
324         isLineNew = false;
325         return this;
326     }
327 
328     /**
329      * Writes an integer.
330      */
331     @Override
332     public CodeWriter write(int i) {
333         writeIndent();
334         out.print(i);
335         colNo += Integer.toString(i).length();
336         isLineNew = false;
337         return this;
338     }
339 
340     @Override
341     public CodeWriter write(List<? extends Codeable> codeables) {
342         if (codeables != null) {
343             writeIndent();
344             for (Codeable element : codeables) {
345                 write(element);
346             }
347         }
348         return this;
349     }
350 
351     /**
352      * Writes an object.
353      */
354     @Override
355     public CodeWriter write(Object o) {
356         if (o != null) {
357             writeIndent();
358             if (o instanceof Codeable) {
359                 ((Codeable) o).toCode(this);
360             } else {
361                 out.print(o);
362                 colNo += o.toString().length();
363                 isLineNew = false;
364             }
365         }
366         return this;
367     }
368 
369     /**
370      * Writes a string.
371      */
372     @Override
373     public CodeWriter write(String s) {
374         if (s != null) {
375             writeIndent();
376             out.print(s);
377             colNo += s.length();
378             isLineNew = false;
379         }
380 
381         return this;
382     }
383 
384     private void flushQueue() {
385         while (!queue.isEmpty()) {
386             Codeable c = queue.remove(0);
387             write(c);
388         }
389     }
390 
391     private void writeIndent() {
392         if (indentationQueue != 0 && !isLineNew()) {
393             throw new IllegalStateException( //
394                     "Not expecting non-empty indentation queue on non-empty line.");
395         }
396 
397         for (int i = 0; i < indentationQueue; ++i) {
398             out.print("    ");
399         }
400         indentationQueue = 0;
401     }
402 
403 }