001 package ezvcard.io;
002
003 import java.io.IOException;
004 import java.io.Writer;
005
006 /*
007 Copyright (c) 2012, Michael Angstadt
008 All rights reserved.
009
010 Redistribution and use in source and binary forms, with or without
011 modification, are permitted provided that the following conditions are met:
012
013 1. Redistributions of source code must retain the above copyright notice, this
014 list of conditions and the following disclaimer.
015 2. Redistributions in binary form must reproduce the above copyright notice,
016 this list of conditions and the following disclaimer in the documentation
017 and/or other materials provided with the distribution.
018
019 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
026 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029
030 The views and conclusions contained in the software and documentation are those
031 of the authors and should not be interpreted as representing official policies,
032 either expressed or implied, of the FreeBSD Project.
033 */
034
035 /**
036 * Automatically folds lines as they are written.
037 * @author Michael Angstadt
038 */
039 public class FoldedLineWriter extends Writer {
040 private int curLineLength = 0;
041 private int lineLength;
042 private String indent;
043 private String newline;
044 private final Writer writer;
045
046 /**
047 * @param writer the writer object to wrap
048 * @param lineLength the maximum length a line can be before it is folded
049 * (excluding the newline)
050 * @param indent the string to prepend to each folded line (e.g. a single
051 * space character)
052 * @param newline the newline sequence to use (e.g. "\r\n")
053 * @throws IllegalArgumentException if the line length is less than or equal
054 * to zero
055 * @throws IllegalArgumentException if the length of the indent string is
056 * greater than the max line length
057 */
058 public FoldedLineWriter(Writer writer, int lineLength, String indent, String newline) {
059 setLineLength(lineLength);
060 setIndent(indent);
061 this.writer = writer;
062 this.newline = newline;
063 }
064
065 /**
066 * Writes a string of text, followed by a newline.
067 * @param str the text to write
068 * @throws IOException if there's a problem writing to the output stream
069 */
070 public void writeln(String str) throws IOException {
071 write(str);
072 write(newline);
073 }
074
075 @Override
076 public void write(char buf[], int start, int end) throws IOException {
077 write(buf, start, end, lineLength, indent);
078 }
079
080 /**
081 * Writes a portion of an array of characters.
082 * @param buf the array of characters
083 * @param start the offset from which to start writing characters
084 * @param end the number of characters to write
085 * @param lineLength the maximum length a line can be before it is folded
086 * (excluding the newline)
087 * @param indent the indent string to use (e.g. a single space character)
088 * @throws IOException if there's a problem writing to the output stream
089 */
090 public void write(char buf[], int start, int end, int lineLength, String indent) throws IOException {
091 for (int i = start; i < end; i++) {
092 char c = buf[i];
093 if (c == '\n') {
094 writer.write(buf, start, i - start + 1);
095 curLineLength = 0;
096 start = i + 1;
097 } else if (c == '\r') {
098 if (i == end - 1 || buf[i + 1] != '\n') {
099 writer.write(buf, start, i - start + 1);
100 curLineLength = 0;
101 start = i + 1;
102 } else {
103 curLineLength++;
104 }
105 } else if (curLineLength >= lineLength) {
106 //if the last characters on the line are whitespace, then exceed the max line length in order to include the whitespace on the same line
107 //otherwise it will be lost because it will merge with the padding on the next line
108 if (Character.isWhitespace(c)) {
109 while (Character.isWhitespace(c) && i < end - 1) {
110 i++;
111 c = buf[i];
112 }
113 if (i == end - 1) {
114 //the rest of the char array is whitespace, so leave the loop
115 break;
116 }
117 }
118
119 writer.write(buf, start, i - start);
120 String s = newline + indent;
121 writer.write(s.toCharArray(), 0, s.length());
122 start = i;
123 curLineLength = indent.length() + 1;
124 } else {
125 curLineLength++;
126 }
127 }
128 writer.write(buf, start, end - start);
129 }
130
131 @Override
132 public void close() throws IOException {
133 writer.close();
134 }
135
136 @Override
137 public void flush() throws IOException {
138 writer.flush();
139 }
140
141 /**
142 * Gets the maximum length a line can be before it is folded (excluding the
143 * newline).
144 * @return the line length
145 */
146 public int getLineLength() {
147 return lineLength;
148 }
149
150 /**
151 * Sets the maximum length a line can be before it is folded (excluding the
152 * newline).
153 * @param lineLength the line length
154 * @throws IllegalArgumentException if the line length is less than or equal
155 * to zero
156 */
157 public void setLineLength(int lineLength) {
158 if (lineLength <= 0) {
159 throw new IllegalArgumentException("Line length must be greater than 0.");
160 }
161 this.lineLength = lineLength;
162 }
163
164 /**
165 * Gets the string that is prepended to each folded line.
166 * @return the indent string
167 */
168 public String getIndent() {
169 return indent;
170 }
171
172 /**
173 * Sets the string that is prepended to each folded line.
174 * @param indent the indent string (e.g. a single space character)
175 * @throws IllegalArgumentException if the length of the indent string is
176 * greater than the max line length
177 */
178 public void setIndent(String indent) {
179 if (indent.length() >= lineLength) {
180 throw new IllegalArgumentException("The length of the indent string must be less than the max line length.");
181 }
182 this.indent = indent;
183 }
184
185 /**
186 * Gets the newline sequence that is used to separate lines.
187 * @return the newline sequence
188 */
189 public String getNewline() {
190 return newline;
191 }
192
193 /**
194 * Sets the newline sequence that is used to separate lines
195 * @param newline the newline sequence
196 */
197 public void setNewline(String newline) {
198 this.newline = newline;
199 }
200 }