forgot patterns index
[ical4j.git] / source / net / fortuna / ical4j / data / UnfoldingReader.java
blob57d234c1a94b8cad935e480e0276c98adbc899f9
1 /**
2 * Copyright (c) 2009, Ben Fortuna
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
9 * o Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * o Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * o Neither the name of Ben Fortuna nor the names of any other contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 package net.fortuna.ical4j.data;
34 import java.io.IOException;
35 import java.io.PushbackReader;
36 import java.io.Reader;
37 import java.util.Arrays;
39 import net.fortuna.ical4j.util.CompatibilityHints;
41 import org.apache.commons.logging.Log;
42 import org.apache.commons.logging.LogFactory;
44 /**
45 * <pre>
46 * $Id: UnfoldingReader.java,v 1.30 2009/06/16 10:52:00 fortuna Exp $ [06-Apr-2004]
47 * </pre>
49 * A reader which performs iCalendar unfolding as it reads. Note that unfolding rules may be "relaxed" to allow
50 * unfolding of non-conformant *.ics files. By specifying the system property "ical4j.unfolding.relaxed=true" iCalendar
51 * files created with Mozilla Calendar/Sunbird may be correctly unfolded.
53 * To wrap this reader with a {@link java.io.BufferedReader} you must ensure you specify an identical buffer size
54 * to that used in the {@link java.io.BufferedReader}.
56 * @author Ben Fortuna
58 public class UnfoldingReader extends PushbackReader {
60 private Log log = LogFactory.getLog(UnfoldingReader.class);
62 /**
63 * The pattern used to identify a fold in an iCalendar data stream.
65 private static final char[] DEFAULT_FOLD_PATTERN_1 = { '\r', '\n', ' ' };
67 /**
68 * The pattern used to identify a fold in Microsoft Outlook 2007.
69 */
70 private static final char[] DEFAULT_FOLD_PATTERN_2 = { '\r', '\n', '\t' };
72 /**
73 * The pattern used to identify a fold in Mozilla Calendar/Sunbird and KOrganizer.
75 private static final char[] RELAXED_FOLD_PATTERN_1 = { '\n', ' ' };
77 /**
78 * The pattern used to identify a fold in Microsoft Outlook 2007.
79 */
80 private static final char[] RELAXED_FOLD_PATTERN_2 = { '\n', '\t' };
82 private char[][] patterns;
84 private char[][] buffers;
86 private int linesUnfolded;
88 private int maxPatternLength = 0;
90 /**
91 * Creates a new unfolding reader instance. Relaxed unfolding flag is read from system property.
92 * @param in the reader to unfold from
94 public UnfoldingReader(final Reader in) {
95 this(in, DEFAULT_FOLD_PATTERN_1.length, CompatibilityHints
96 .isHintEnabled(CompatibilityHints.KEY_RELAXED_UNFOLDING));
99 /**
100 * @param in reader source for data
101 * @param size the buffer size
103 public UnfoldingReader(final Reader in, int size) {
104 this(in, size, CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_UNFOLDING));
108 * @param in reader source for data
109 * @param relaxed indicates whether relaxed unfolding is enabled
111 public UnfoldingReader(final Reader in, boolean relaxed) {
112 this(in, DEFAULT_FOLD_PATTERN_1.length, relaxed);
116 * Creates a new unfolding reader instance.
117 * @param in a reader to read from
118 * @param size the buffer size
119 * @param relaxed specifies whether unfolding is relaxed
121 public UnfoldingReader(final Reader in, int size, final boolean relaxed) {
122 super(in, size);
123 if (relaxed) {
124 patterns = new char[4][];
125 patterns[0] = DEFAULT_FOLD_PATTERN_1;
126 patterns[1] = DEFAULT_FOLD_PATTERN_2;
127 patterns[2] = RELAXED_FOLD_PATTERN_1;
128 patterns[3] = RELAXED_FOLD_PATTERN_2;
130 else {
131 patterns = new char[2][];
132 patterns[0] = DEFAULT_FOLD_PATTERN_1;
133 patterns[1] = DEFAULT_FOLD_PATTERN_2;
135 buffers = new char[patterns.length][];
136 for (int i = 0; i < patterns.length; i++) {
137 buffers[i] = new char[patterns[i].length];
138 maxPatternLength = Math.max(maxPatternLength, patterns[i].length);
143 * @return number of lines unfolded so far while reading
145 public final int getLinesUnfolded() {
146 return linesUnfolded;
150 * {@inheritDoc}
152 public final int read() throws IOException {
153 final int c = super.read();
154 boolean doUnfold = false;
155 for (int i = 0; i < patterns.length; i++) {
156 if (c == patterns[i][0]) {
157 doUnfold = true;
158 break;
161 if (!doUnfold) {
162 return c;
164 else {
165 unread(c);
168 unfold();
170 return super.read();
174 * {@inheritDoc}
176 public int read(final char[] cbuf, final int off, final int len) throws IOException {
177 final int read = super.read(cbuf, off, len);
178 boolean doUnfold = false;
179 for (int i = 0; i < patterns.length; i++) {
180 if (read > 0 && cbuf[0] == patterns[i][0]) {
181 doUnfold = true;
182 break;
184 else {
185 for (int j = 0; j < read; j++) {
186 if (cbuf[j] == patterns[i][0]) {
187 unread(cbuf, j, read - j);
188 return j;
193 if (!doUnfold) {
194 return read;
196 else {
197 unread(cbuf, off, read);
200 unfold();
202 return super.read(cbuf, off, maxPatternLength);
205 private void unfold() throws IOException {
206 // need to loop since one line fold might be directly followed by another
207 boolean didUnfold;
208 do {
209 didUnfold = false;
211 for (int i = 0; i < buffers.length; i++) {
212 final int read = super.read(buffers[i], 0, buffers[i].length);
213 if (read > 0) {
214 if (!Arrays.equals(patterns[i], buffers[i])) {
215 unread(buffers[i], 0, read);
217 else {
218 if (log.isTraceEnabled()) {
219 log.trace("Unfolding...");
221 linesUnfolded++;
222 didUnfold = true;
225 // else {
226 // return read;
227 // }
230 while (didUnfold);