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 }