1 /*
2 * Copyright (C) 2010, 2013 Marc Strapetz <marc.strapetz@syntevo.com>
3 * and other copyright owners as documented in the project's IP log.
4 *
5 * This program and the accompanying materials are made available
6 * under the terms of the Eclipse Distribution License v1.0 which
7 * accompanies this distribution, is reproduced below, and is
8 * available at http://www.eclipse.org/org/documents/edl-v10.php
9 *
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or
13 * without modification, are permitted provided that the following
14 * conditions are met:
15 *
16 * - Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 *
19 * - Redistributions in binary form must reproduce the above
20 * copyright notice, this list of conditions and the following
21 * disclaimer in the documentation and/or other materials provided
22 * with the distribution.
23 *
24 * - Neither the name of the Eclipse Foundation, Inc. nor the
25 * names of its contributors may be used to endorse or promote
26 * products derived from this software without specific prior
27 * written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 */
43
44 package org.eclipse.jgit.util.io;
45
46 import java.io.IOException;
47 import java.io.InputStream;
48
49 import org.eclipse.jgit.diff.RawText;
50
51 /**
52 * An input stream which canonicalizes EOLs bytes on the fly to '\n'.
53 *
54 * Optionally, a binary check on the first 8000 bytes is performed
55 * and in case of binary files, canonicalization is turned off
56 * (for the complete file).
57 */
58 public class EolCanonicalizingInputStream extends InputStream {
59 private final byte[] single = new byte[1];
60
61 private final byte[] buf = new byte[8096];
62
63 private final InputStream in;
64
65 private int cnt;
66
67 private int ptr;
68
69 private boolean isBinary;
70
71 private boolean detectBinary;
72
73 private boolean abortIfBinary;
74
75 /**
76 * A special exception thrown when {@link EolCanonicalizingInputStream} is
77 * told to throw an exception when attempting to read a binary file. The
78 * exception may be thrown at any stage during reading.
79 *
80 * @since 3.3
81 */
82 public static class IsBinaryException extends IOException {
83 private static final long serialVersionUID = 1L;
84
85 IsBinaryException() {
86 super();
87 }
88 }
89
90 /**
91 * Creates a new InputStream, wrapping the specified stream
92 *
93 * @param in
94 * raw input stream
95 * @param detectBinary
96 * whether binaries should be detected
97 * @since 2.0
98 */
99 public EolCanonicalizingInputStream(InputStream in, boolean detectBinary) {
100 this(in, detectBinary, false);
101 }
102
103 /**
104 * Creates a new InputStream, wrapping the specified stream
105 *
106 * @param in
107 * raw input stream
108 * @param detectBinary
109 * whether binaries should be detected
110 * @param abortIfBinary
111 * throw an IOException if the file is binary
112 * @since 3.3
113 */
114 public EolCanonicalizingInputStream(InputStream in, boolean detectBinary,
115 boolean abortIfBinary) {
116 this.in = in;
117 this.detectBinary = detectBinary;
118 this.abortIfBinary = abortIfBinary;
119 }
120
121 @Override
122 public int read() throws IOException {
123 final int read = read(single, 0, 1);
124 return read == 1 ? single[0] & 0xff : -1;
125 }
126
127 @Override
128 public int read(byte[] bs, final int off, final int len) throws IOException {
129 if (len == 0)
130 return 0;
131
132 if (cnt == -1)
133 return -1;
134
135 int i = off;
136 final int end = off + len;
137
138 while (i < end) {
139 if (ptr == cnt && !fillBuffer()) {
140 break;
141 }
142
143 byte b = buf[ptr++];
144 if (isBinary || b != '\r') {
145 // Logic for binary files ends here
146 bs[i++] = b;
147 continue;
148 }
149
150 if (ptr == cnt && !fillBuffer()) {
151 bs[i++] = '\r';
152 break;
153 }
154
155 if (buf[ptr] == '\n') {
156 bs[i++] = '\n';
157 ptr++;
158 } else
159 bs[i++] = '\r';
160 }
161
162 return i == off ? -1 : i - off;
163 }
164
165 /**
166 * @return true if the stream has detected as a binary so far
167 * @since 3.3
168 */
169 public boolean isBinary() {
170 return isBinary;
171 }
172
173 @Override
174 public void close() throws IOException {
175 in.close();
176 }
177
178 private boolean fillBuffer() throws IOException {
179 cnt = in.read(buf, 0, buf.length);
180 if (cnt < 1)
181 return false;
182 if (detectBinary) {
183 isBinary = RawText.isBinary(buf, cnt);
184 detectBinary = false;
185 if (isBinary && abortIfBinary)
186 throw new IsBinaryException();
187 }
188 ptr = 0;
189 return true;
190 }
191 }