BomStrippingInputStreamReader.java
001 /*
002  *  Copyright (c) 1995-2012, The University of Sheffield. See the file
003  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
004  *
005  *  This file is part of GATE (see http://gate.ac.uk/), and is free
006  *  software, licenced under the GNU Library General Public License,
007  *  Version 2, June 1991 (in the distribution as file licence.html,
008  *  and also available at http://gate.ac.uk/gate/licence.html).
009  *  
010  *  Benson Margulies 28/07/2010
011  *
012  *  $Id: BomStrippingInputStreamReader.java 17530 2014-03-04 15:57:43Z markagreenwood $
013  */
014 
015 package gate.util;
016 
017 import java.io.BufferedReader;
018 import java.io.IOException;
019 import java.io.InputStream;
020 import java.io.InputStreamReader;
021 import java.io.UnsupportedEncodingException;
022 import java.nio.CharBuffer;
023 import java.nio.charset.Charset;
024 import java.nio.charset.CharsetDecoder;
025 
026 /**
027  * StreamReader that removes the Unicode BOM, even when Sun/Oracle is
028  * too lazy to do so. Since a buffer is required, and since most of GATE
029  * was coded to use BufferedReaders around the InputStreamReader, this
030  * 'isa' BufferedReader.
031  *
032  * Note that there are differences in exception behaviour on the
033  * different InputStreamReader constructors, so this has to be careful
034  * to call the right one.
035  *
036  */
037 public class BomStrippingInputStreamReader extends BufferedReader {
038   private IOException pendingConstructionException;
039 
040   private boolean pendingEOF;
041 
042   private boolean pendingChecked;
043 
044   public BomStrippingInputStreamReader(InputStream in) {
045     this(new InputStreamReader(in));
046   }
047 
048   public BomStrippingInputStreamReader(InputStream in, String charsetName)
049           throws UnsupportedEncodingException {
050     this(new InputStreamReader(in, charsetName));
051   }
052 
053   public BomStrippingInputStreamReader(InputStream in, String charsetName,
054           int bufferSizethrows UnsupportedEncodingException {
055     this(new InputStreamReader(in, charsetName), bufferSize);
056   }
057 
058   public BomStrippingInputStreamReader(InputStream in, Charset cs) {
059     this(new InputStreamReader(in, cs));
060   }
061 
062   public BomStrippingInputStreamReader(InputStream in, int bufferSize) {
063     this(new InputStreamReader(in), bufferSize);
064   }
065 
066   public BomStrippingInputStreamReader(InputStream in, CharsetDecoder dec,
067           int bufferSize) {
068     this(new InputStreamReader(in, dec), bufferSize);
069   }
070 
071   private BomStrippingInputStreamReader(InputStreamReader isr, int bufferSize) {
072     super(isr, bufferSize);
073     stripBomIfPresent();
074   }
075 
076   private BomStrippingInputStreamReader(InputStreamReader isr) {
077     super(isr);
078     stripBomIfPresent();
079   }
080 
081   public BomStrippingInputStreamReader(InputStream in, CharsetDecoder dec) {
082     super(new InputStreamReader(in, dec));
083     stripBomIfPresent();
084   }
085 
086   /**
087    * Checks whether the first character is BOM and positions the input stream 
088    * past it, if that's the case.
089    */
090   private void stripBomIfPresent() {
091     try {
092       super.mark(1);
093       int firstChar = super.read();
094       if(firstChar == -1) {
095         pendingEOF = true/*
096                             * If we hit EOF, note to return it from next
097                             * call.
098                             */
099       }
100       else if(firstChar != 0xfeff) {
101         super.reset()/* if we read non-BOM, push back */
102       }
103       /* otherwise leave it consumed */
104 
105     }
106     catch(IOException e) {
107       pendingConstructionException = e;
108     }
109   }
110 
111   @Override
112   public int hashCode() {
113     return super.hashCode();
114   }
115 
116   @Override
117   public boolean equals(Object obj) {
118     return super.equals(obj);
119   }
120 
121   private boolean checkPending() throws IOException {
122     if(!pendingChecked) {
123       if(pendingEOF) {
124         return true;
125       }
126       else if(pendingConstructionException != null) {
127         throw pendingConstructionException;
128       }
129       pendingChecked = true;
130     }
131     return false;
132   }
133 
134   @Override
135   public int read(CharBuffer targetthrows IOException {
136     if(checkPending()) {
137       return -1;
138     }
139     return super.read(target);
140   }
141 
142   @Override
143   public int read(char[] cbufthrows IOException {
144     if(checkPending()) {
145       return -1;
146     }
147     return super.read(cbuf);
148   }
149 
150   @Override
151   public int read() throws IOException {
152     if(checkPending()) {
153       return -1;
154     }
155     return super.read();
156   }
157 
158   @Override
159   public int read(char[] cbuf, int off, int lenthrows IOException {
160     if(checkPending()) {
161       return -1;
162     }
163     return super.read(cbuf, off, len);
164   }
165 
166   @Override
167   public String readLine() throws IOException {
168     if(checkPending()) {
169       return null;
170     }
171     return super.readLine();
172   }
173 
174   @Override
175   public long skip(long nthrows IOException {
176     if(checkPending()) {
177       return 0;
178     }
179     return super.skip(n);
180   }
181 
182   @Override
183   public boolean ready() throws IOException {
184     if(checkPending()) {
185       return false;
186     }
187     return super.ready();
188   }
189 
190   @Override
191   public boolean markSupported() {
192     return super.markSupported();
193   }
194 
195   @Override
196   public void mark(int readAheadLimitthrows IOException {
197     checkPending();
198     super.mark(readAheadLimit);
199   }
200 
201   @Override
202   public void reset() throws IOException {
203     checkPending();
204     super.reset();
205   }
206 
207   @Override
208   public void close() throws IOException {
209     // go ahead and close on this call even if we have an IOException
210     // sitting around.
211     super.close();
212   }
213 
214 }