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 }