AbstractOutput.java
001 /* This file is part of the project "Hilbert II" - http://www.qedeq.org
002  *
003  * Copyright 2000-2013,  Michael Meyling <mime@qedeq.org>.
004  *
005  * "Hilbert II" is free software; you can redistribute
006  * it and/or modify it under the terms of the GNU General Public
007  * License as published by the Free Software Foundation; either
008  * version 2 of the License, or (at your option) any later version.
009  *
010  * This program is distributed in the hope that it will be useful,
011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013  * GNU General Public License for more details.
014  */
015 
016 package org.qedeq.base.io;
017 
018 import org.qedeq.base.utility.Splitter;
019 import org.qedeq.base.utility.StringUtility;
020 
021 
022 /**
023  * Wraps a text output stream.
024  *
025  @author  Michael Meyling
026  */
027 public abstract class AbstractOutput {
028 
029     /** Tab level. */
030     private StringBuffer spaces = new StringBuffer();
031 
032     /** Break at this column if greater zero. */
033     private int breakAt;
034 
035     /** Tab level of current line. This is equal to spaces before any character is
036      * written. After writing to the current line this value is fixed and doesn't change even
037      * if the tab level is changed.
038      */
039     private String spacesForCurrentLine = "";
040 
041     /** Current column. */
042     private int col;
043 
044     /** Token buffer. */
045     private StringBuffer tokenBuffer = new StringBuffer();
046 
047     /** Whitespace buffer. */
048     private StringBuffer wsBuffer = new StringBuffer();
049 
050     /**
051      * Constructor.
052      */
053     public AbstractOutput() {
054     }
055 
056     /**
057      * Add whitespace to output.
058      *
059      @param   ws  Add this whitespace.
060      */
061     public void addWs(final String ws) {
062         final String[] lines = StringUtility.split(ws, "\n");
063         for (int i = 0; i < lines.length; i++) {
064             if (i > 0) {
065                 println();
066             }
067             addWsWithoutCR(lines[i]);
068         }
069     }
070 
071     /**
072      * Add whitespace to output. Must not contain CRs.
073      *
074      @param   ws  Add this whitespace.
075      */
076     private void addWsWithoutCR(final String ws) {
077         if (tokenBuffer.length() 0) {
078             if (fits(wsBuffer.length() + tokenBuffer.length())) {
079                 if (col == 0) {
080                     appendSpaces();
081                 }
082                 append(wsBuffer.toString());
083                 col += wsBuffer.length();
084                 append(tokenBuffer.toString());
085                 col += tokenBuffer.length();
086             else {
087                 // forget non fitting part of white space
088                 if (col != 0) {
089                     appendFittingPart(wsBuffer.toString());
090                     append("\n");
091                 }
092                 col = 0;
093                 appendSpaces();
094                 append(tokenBuffer.toString());
095                 col += tokenBuffer.length();
096             }
097             wsBuffer.setLength(0);
098             tokenBuffer.setLength(0);
099         }
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 == && 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 + < 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 <= || 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 == && 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 }