001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.maven.doxia.util;
020
021import java.io.BufferedWriter;
022import java.io.IOException;
023import java.io.Writer;
024
025/**
026 * Allows to specify the line-length of an output writer.
027 */
028public class LineBreaker {
029    /** The default maximal line length. */
030    public static final int DEFAULT_MAX_LINE_LENGTH = 78;
031
032    /** The system dependent EOL. */
033    private static final String EOL = System.getProperty("line.separator");
034
035    /** The destination writer. */
036    private Writer destination;
037
038    /** The writer to use. */
039    private BufferedWriter writer;
040
041    /** The maximal line length. */
042    private int maxLineLength;
043
044    /** The current line length. */
045    private int lineLength = 0;
046
047    /** The string buffer to store the current text. */
048    private StringBuilder word = new StringBuilder(1024);
049
050    /**
051     * Constructs a new LineBreaker with DEFAULT_MAX_LINE_LENGTH.
052     *
053     * @param out The writer to use.
054     */
055    public LineBreaker(Writer out) {
056        this(out, DEFAULT_MAX_LINE_LENGTH);
057    }
058
059    /**
060     * Constructs a new LineBreaker with the given max line length.
061     *
062     * @param out The writer to use.
063     * @param max The maximal line length.
064     */
065    public LineBreaker(Writer out, int max) {
066        if (max <= 0) {
067            throw new IllegalArgumentException("max must be a positive integer");
068        }
069
070        destination = out;
071        this.maxLineLength = max;
072        writer = new BufferedWriter(out);
073    }
074
075    /**
076     * Returns the current destination writer.
077     *
078     * @return The destination.
079     */
080    public Writer getDestination() {
081        return destination;
082    }
083
084    /**
085     * Writes the given text to the writer. White space is not preserved.
086     *
087     * @param text The text to write.
088     * @throws java.io.IOException if there's a problem writing the text.
089     */
090    public void write(String text) throws IOException {
091        write(text, /*preserveSpace*/ false);
092    }
093
094    /**
095     * Writes the given text to the writer.
096     *
097     * @param text The text to write.
098     * @param preserveSpace True to preserve white space.
099     */
100    public void write(String text, boolean preserveSpace) {
101        int length = text.length();
102
103        try {
104            for (int i = 0; i < length; ++i) {
105                char c = text.charAt(i);
106
107                switch (c) {
108                    case ' ':
109                        if (preserveSpace) {
110                            word.append(c);
111                        } else {
112                            writeWord();
113                        }
114                        break;
115
116                    case '\r':
117                        // if \r\n (windows) then just pass along \n
118                        if (i + 1 < length && text.charAt(i + 1) == '\n') {
119                            break;
120                        }
121
122                    case '\n':
123                        writeWord();
124                        writer.write(EOL);
125                        lineLength = 0;
126                        break;
127
128                    default:
129                        word.append(c);
130                }
131            }
132        } catch (Exception e) {
133            // TODO: log
134        }
135    }
136
137    /**
138     * Write out the current StringBuilder and flush the writer.
139     * Any IOException will be swallowed.
140     */
141    public void flush() {
142        try {
143            writeWord();
144            writer.flush();
145        } catch (IOException e) {
146            // TODO: log
147        }
148    }
149
150    /**
151     * Writes the current StringBuilder to the writer.
152     *
153     * @throws IOException if an exception occurs during writing.
154     */
155    private void writeWord() throws IOException {
156        int length = word.length();
157        if (length > 0) {
158            if (lineLength > 0) {
159                if (lineLength + 1 + length > maxLineLength) {
160                    writer.write(EOL);
161                    lineLength = 0;
162                } else {
163                    writer.write(' ');
164                    ++lineLength;
165                }
166            }
167
168            writer.write(word.toString());
169            word.setLength(0);
170
171            lineLength += length;
172        }
173    }
174
175    /**
176     * Close the writer.
177     */
178    public void close() {
179        if (writer == null) {
180            return;
181        }
182
183        try {
184            writer.close();
185        } catch (IOException ex) {
186            // ignore
187        }
188    }
189}