View Javadoc

1   /* This file is part of the project "Hilbert II" - http://www.qedeq.org" target="alexandria_uri">http://www.qedeq.org
2    *
3    * Copyright 2000-2014,  Michael Meyling <mime@qedeq.org>.
4    *
5    * "Hilbert II" is free software; you can redistribute
6    * it and/or modify it under the terms of the GNU General Public
7    * License as published by the Free Software Foundation; either
8    * version 2 of the License, or (at your option) any later version.
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13   * GNU General Public License for more details.
14   */
15  
16  package org.qedeq.base.io;
17  
18  import org.qedeq.base.utility.Splitter;
19  import org.qedeq.base.utility.StringUtility;
20  
21  
22  /**
23   * Wraps a text output stream.
24   *
25   * @author  Michael Meyling
26   */
27  public abstract class AbstractOutput {
28  
29      /** Tab level. */
30      private StringBuffer spaces = new StringBuffer();
31  
32      /** Break at this column if greater zero. */
33      private int breakAt;
34  
35      /** Tab level of current line. This is equal to spaces before any character is
36       * written. After writing to the current line this value is fixed and doesn't change even
37       * if the tab level is changed.
38       */
39      private String spacesForCurrentLine = "";
40  
41      /** Current column. */
42      private int col;
43  
44      /** Token buffer. */
45      private StringBuffer tokenBuffer = new StringBuffer();
46  
47      /** Whitespace buffer. */
48      private StringBuffer wsBuffer = new StringBuffer();
49  
50      /**
51       * Constructor.
52       */
53      public AbstractOutput() {
54      }
55  
56      /**
57       * Add whitespace to output.
58       *
59       * @param   ws  Add this whitespace.
60       */
61      public void addWs(final String ws) {
62          final String[] lines = StringUtility.split(ws, "\n");
63          for (int i = 0; i < lines.length; i++) {
64              if (i > 0) {
65                  println();
66              }
67              addWsWithoutCR(lines[i]);
68          }
69      }
70  
71      /**
72       * Add whitespace to output. Must not contain CRs.
73       *
74       * @param   ws  Add this whitespace.
75       */
76      private void addWsWithoutCR(final String ws) {
77          if (tokenBuffer.length() > 0) {
78              if (fits(wsBuffer.length() + tokenBuffer.length())) {
79                  if (col == 0) {
80                      appendSpaces();
81                  }
82                  append(wsBuffer.toString());
83                  col += wsBuffer.length();
84                  append(tokenBuffer.toString());
85                  col += tokenBuffer.length();
86              } else {
87                  // forget non fitting part of white space
88                  if (col != 0) {
89                      appendFittingPart(wsBuffer.toString());
90                      append("\n");
91                  }
92                  col = 0;
93                  appendSpaces();
94                  append(tokenBuffer.toString());
95                  col += tokenBuffer.length();
96              }
97              wsBuffer.setLength(0);
98              tokenBuffer.setLength(0);
99          }
100         wsBuffer.append(ws);
101     }
102 
103     /**
104      * Append token to output.
105      *
106      * @param   part    Add this part.
107      */
108     public void addToken(final String part) {
109         // remember tabular spaces when we start writing
110         if (col == 0 && part.length() > 0) {
111             setTabLevel();
112         }
113         tokenBuffer.append(part);
114     }
115 
116     /**
117      * Flush output.
118      */
119     public void flush() {
120         addWsWithoutCR("");
121         appendFittingPart(wsBuffer.toString());
122         wsBuffer.setLength(0);
123     }
124 
125     /**
126      * Append a part of given text so that the current maximum column is not exceeded.
127      *
128      * @param   txt     Write this text.
129      * @return  Length of written characters.
130      */
131     private int appendFittingPart(final String txt) {
132         final int columnsLeft = columnsLeft();
133         if (columnsLeft > 0) {
134             final String part = StringUtility.substring(txt, 0, columnsLeft);
135             append(part);
136             col += part.length();
137             return part.length();
138         } else if (columnsLeft < 0) {
139             append(txt);
140             col += txt.length();
141             return txt.length();
142         }
143         return 0;
144     }
145 
146     /**
147      * Print character to output.
148      *
149      * @param   c   Append this.
150      */
151     public void print(final char c) {
152         print("" + c);
153     }
154 
155     /**
156      * Print text and split at white space if text doesn't fit within maximum column size.
157      * Also flushes output.
158      *
159      * @param   text    Append this.
160      */
161     public void print(final String text) {
162         flush();
163         if (text == null) {
164             addToken("null");
165         } else {
166             final String[] lines = StringUtility.split(text, "\n");
167             for (int i = 0; i < lines.length; i++) {
168                 final Splitter split = new Splitter(lines[i]);
169                 while (split.hasNext()) {
170                     final String token = split.nextToken();
171                     final boolean isWhitespace = token.trim().length() == 0;
172                     if (isWhitespace) {
173                         addWsWithoutCR(token);
174                     } else {
175                         addToken(token);
176                     }
177                 }
178                 if (i + 1 < lines.length) {
179                     println();
180                 }
181             }
182         }
183         flush();
184     }
185 
186     /**
187      * Append text directly to output device.
188      *
189      * @param   text    Append this text.
190      */
191     public abstract void append(final String text);
192 
193     /**
194      * Get writing position.
195      *
196      * @return  Writing position.
197      */
198     public abstract long getPosition();
199 
200     /**
201      * Print spaces and text to output.
202      *
203      * @param   text    Append this.
204      */
205     public void printWithoutSplit(final String text) {
206         flush();
207         if (text == null) {
208             return;
209         }
210         if (col == 0) {
211             if (text.length() > 0) {
212                 // remember tabular spaces when we start writing
213                 setTabLevel();
214                 appendSpaces();
215             }
216         } else if (!fits(text)) {
217             println();
218             appendSpaces();
219         }
220         append(text);
221         col += text.length();
222     }
223 
224     /**
225      * Does the text fit to current line?
226      *
227      * @param   text    Check if this text could be appended without line break.
228      * @return  Does it fit?
229      */
230     private boolean fits(final String text) {
231         if (text == null) {
232             return true;
233         }
234         return fits(text.length());
235     }
236 
237     /**
238      * Does a text with given length fit to current line?
239      * TODO 20110104 m31: should't we use spacesForCurrentLine also?
240      *
241      * @param   length    Check if a text of this length could be appended without line break.
242      * @return  Does it fit?
243      */
244     private boolean fits(final int length) {
245         return breakAt <= 0 || col + length <= breakAt;
246     }
247 
248     /**
249      * How many columns have we left?
250      *
251      * @return  Columns left. Returns -1 if there is no limit.
252      */
253     public int columnsLeft() {
254         if (breakAt <= 0) {
255             return -1;
256         } else {
257             return Math.max(0, breakAt - col);
258         }
259     }
260 
261     /**
262      * Print object to output.
263      *
264      * @param   object  Append text representation of this.
265      */
266     public void print(final Object object) {
267         print(String.valueOf(object));
268     }
269 
270     /**
271      * Print spaces text and new line to output.
272      *
273      * @param   token   Append this.
274      */
275     public final void println(final String token) {
276         print(token);
277         println();
278     }
279 
280     /**
281      * Print object and new line to output.
282      *
283      * @param   object  Append text representation of this.
284      */
285     public final void println(final Object object) {
286         println(String.valueOf(object));
287     }
288 
289     /**
290      * Print new line to output.
291      */
292     public void println() {
293         flush();
294         if (col == 0 && spaces.toString().trim().length() > 0) {
295             setTabLevel();
296             appendSpaces();
297         }
298         append("\n");
299         col = 0;
300     }
301 
302     /**
303      * Skip until given column. To do this we append spaces.
304      *
305      * @param   column  Skip to this column.
306      */
307     public void skipToColumn(final int column) {
308         for (int i = col; i < column; i++) {
309             printWithoutSplit(" ");
310         }
311     }
312 
313     /**
314      * Reset tab level to zero.
315      */
316     public final void clearLevel() {
317         // flush();
318         spaces.setLength(0);
319     }
320 
321     /**
322      * Decrement tab level.
323      */
324     public final void popLevel() {
325         if (spaces.length() > 0) {
326             spaces.setLength(spaces.length() - 2);
327         }
328     }
329 
330     /**
331      * Decrement tab level.
332      *
333      * @param   characters  Number of characters to reduce from tab level.
334      */
335     public final void popLevel(final int characters) {
336         if (spaces.length() > 0) {
337             spaces.setLength(Math.max(spaces.length() - characters, 0));
338         }
339     }
340 
341     /**
342      * Return current tab string.
343      *
344      * @return  Current tab string.
345      */
346     public final String getLevel() {
347         return spaces.toString();
348     }
349 
350     /**
351      * Set current tab string.
352      *
353      * @param   level   Tab string.
354      */
355     public final void setLevel(final String level) {
356         spaces.setLength(0);
357         spaces.append(level);
358     }
359 
360     /**
361      * Increment tab level (this are two spaces).
362      */
363     public final void pushLevel() {
364         spaces.append("  ");
365     }
366 
367     /**
368      * Increment tab level with following symbols.
369      *
370      * @param   symbols Symbols to tab width. Length should be exactly 2 characters!
371      */
372     public final void pushLevel(final String symbols) {
373         spaces.append(symbols);
374     }
375 
376     /**
377      * Set current tab level to current level. Might change unwritten lines.
378      */
379     public final void setTabLevel() {
380         spacesForCurrentLine = spaces.toString();
381     }
382 
383     /**
384      * Set number of maximum columns. If possible we break before we reach this column number.
385      * If less or equal to zero no line breaking is done automatically.
386      *
387      * @param   columns Maximum column size.
388      */
389     public void setColumns(final int columns) {
390         if (columns < 0) {
391             breakAt = 0;
392         } else {
393             breakAt = columns;
394         }
395     }
396 
397     /**
398      * Return number of maximum columns. If equal to zero no line breaking is done automatically.
399      *
400      * @return  Maximum column size.
401      */
402     public final int getColumns() {
403         return breakAt;
404     }
405 
406     /**
407      * Append tabulation and increase current column.
408      */
409     private void appendSpaces() {
410         append(spacesForCurrentLine.toString());
411         col += spacesForCurrentLine.length();
412     }
413 
414 }