2 * Copyright (c) 2009, Ben Fortuna
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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
;
46 * $Id: UnfoldingReader.java,v 1.30 2009/06/16 10:52:00 fortuna Exp $ [06-Apr-2004]
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}.
58 public class UnfoldingReader
extends PushbackReader
{
60 private Log log
= LogFactory
.getLog(UnfoldingReader
.class);
63 * The pattern used to identify a fold in an iCalendar data stream.
65 private static final char[] DEFAULT_FOLD_PATTERN_1
= { '\r', '\n', ' ' };
68 * The pattern used to identify a fold in Microsoft Outlook 2007.
70 private static final char[] DEFAULT_FOLD_PATTERN_2
= { '\r', '\n', '\t' };
73 * The pattern used to identify a fold in Mozilla Calendar/Sunbird and KOrganizer.
75 private static final char[] RELAXED_FOLD_PATTERN_1
= { '\n', ' ' };
78 * The pattern used to identify a fold in Microsoft Outlook 2007.
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;
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
));
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
) {
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
;
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
;
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]) {
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]) {
185 for (int j
= 0; j
< read
; j
++) {
186 if (cbuf
[j
] == patterns
[i
][0]) {
187 unread(cbuf
, j
, read
- j
);
197 unread(cbuf
, off
, read
);
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
211 for (int i
= 0; i
< buffers
.length
; i
++) {
212 final int read
= super.read(buffers
[i
], 0, buffers
[i
].length
);
214 if (!Arrays
.equals(patterns
[i
], buffers
[i
])) {
215 unread(buffers
[i
], 0, read
);
218 if (log
.isTraceEnabled()) {
219 log
.trace("Unfolding...");