|
|
@@ -0,0 +1,183 @@ |
|
|
|
package cn.afterturn.easypoi.util; |
|
|
|
|
|
|
|
/** |
|
|
|
* @author by jueyue on 18-11-7. |
|
|
|
*/ |
|
|
|
|
|
|
|
import java.io.IOException; |
|
|
|
import java.io.InputStream; |
|
|
|
import java.io.PushbackInputStream; |
|
|
|
|
|
|
|
public class UnicodeInputStream extends InputStream { |
|
|
|
public static final UnicodeInputStream.UnicodeBOM UTF8_BOM = new UnicodeInputStream.UnicodeBOM("UTF-8", new byte[]{-17, -69, -65}); |
|
|
|
public static final UnicodeInputStream.UnicodeBOM UTF16LE_BOM = new UnicodeInputStream.UnicodeBOM("UTF-16LE", new byte[]{-1, -2}); |
|
|
|
public static final UnicodeInputStream.UnicodeBOM UTF16BE_BOM = new UnicodeInputStream.UnicodeBOM("UTF-16BE", new byte[]{-2, -1}); |
|
|
|
public static final UnicodeInputStream.UnicodeBOM UTF32LE_BOM = new UnicodeInputStream.UnicodeBOM("UTF-32LE", new byte[]{-1, -2, 0, 0}); |
|
|
|
public static final UnicodeInputStream.UnicodeBOM UTF32BE_BOM = new UnicodeInputStream.UnicodeBOM("UTF-32BE", new byte[]{0, 0, -2, -1}); |
|
|
|
private static final int MAX_BOM_SIZE = 4; |
|
|
|
private byte[] buf; |
|
|
|
private int pos; |
|
|
|
private final String encoding; |
|
|
|
private final boolean skipBOM; |
|
|
|
private final PushbackInputStream inputStream; |
|
|
|
|
|
|
|
public UnicodeInputStream(InputStream inputStream) throws IllegalStateException, IOException { |
|
|
|
this(inputStream, true); |
|
|
|
} |
|
|
|
|
|
|
|
public UnicodeInputStream(InputStream inputStream, boolean skipBOM) throws IllegalStateException, IOException { |
|
|
|
this.buf = new byte[MAX_BOM_SIZE]; |
|
|
|
this.pos = 0; |
|
|
|
this.skipBOM = skipBOM; |
|
|
|
this.inputStream = new PushbackInputStream(inputStream, MAX_BOM_SIZE); |
|
|
|
|
|
|
|
try { |
|
|
|
this.encoding = this.readEncoding(); |
|
|
|
} catch (IOException var5) { |
|
|
|
IllegalStateException ex = new IllegalStateException("Could not read BOM from Stream"); |
|
|
|
throw ex; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public boolean isSkipBOM() { |
|
|
|
return this.skipBOM; |
|
|
|
} |
|
|
|
|
|
|
|
public String getEncodingFromStream() { |
|
|
|
return this.encoding; |
|
|
|
} |
|
|
|
|
|
|
|
protected String readEncoding() throws IOException { |
|
|
|
this.pos = 0; |
|
|
|
UnicodeInputStream.UnicodeBOM encoding = null; |
|
|
|
if (this.readByte()) { |
|
|
|
switch(this.buf[0]) { |
|
|
|
case -17: |
|
|
|
encoding = this.match(UTF8_BOM, (UnicodeInputStream.UnicodeBOM)null); |
|
|
|
break; |
|
|
|
case -2: |
|
|
|
encoding = this.match(UTF16BE_BOM, (UnicodeInputStream.UnicodeBOM)null); |
|
|
|
break; |
|
|
|
case -1: |
|
|
|
encoding = this.match(UTF16LE_BOM, (UnicodeInputStream.UnicodeBOM)null); |
|
|
|
if (encoding != null) { |
|
|
|
encoding = this.match(UTF32LE_BOM, encoding); |
|
|
|
} |
|
|
|
break; |
|
|
|
case 0: |
|
|
|
encoding = this.match(UTF32BE_BOM, (UnicodeInputStream.UnicodeBOM)null); |
|
|
|
break; |
|
|
|
default: |
|
|
|
encoding = null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
this.pushback(encoding); |
|
|
|
return encoding != null ? encoding.getEncoding() : null; |
|
|
|
} |
|
|
|
|
|
|
|
private final UnicodeInputStream.UnicodeBOM match(UnicodeInputStream.UnicodeBOM matchEncoding, UnicodeInputStream.UnicodeBOM noMatchEncoding) throws IOException { |
|
|
|
byte[] bom = matchEncoding.getBytes(); |
|
|
|
|
|
|
|
for(int i = 0; i < bom.length; ++i) { |
|
|
|
if (this.pos <= i && !this.readByte()) { |
|
|
|
return noMatchEncoding; |
|
|
|
} |
|
|
|
|
|
|
|
if (bom[i] != this.buf[i]) { |
|
|
|
return noMatchEncoding; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return matchEncoding; |
|
|
|
} |
|
|
|
|
|
|
|
private final boolean readByte() throws IOException { |
|
|
|
int res = this.inputStream.read(); |
|
|
|
if (res == -1) { |
|
|
|
return false; |
|
|
|
} else if (this.pos >= this.buf.length) { |
|
|
|
throw new IOException("BOM read error"); |
|
|
|
} else { |
|
|
|
this.buf[this.pos++] = (byte)res; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private final void pushback(UnicodeInputStream.UnicodeBOM matchBOM) throws IOException { |
|
|
|
int count = this.pos; |
|
|
|
int start = 0; |
|
|
|
if (matchBOM != null && this.skipBOM) { |
|
|
|
start = matchBOM.getBytes().length; |
|
|
|
count = this.pos - start; |
|
|
|
if (count < 0) { |
|
|
|
throw new IllegalStateException("Match has more bytes than available!"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
this.inputStream.unread(this.buf, start, count); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void close() throws IOException { |
|
|
|
this.inputStream.close(); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public int available() throws IOException { |
|
|
|
return this.inputStream.available(); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void mark(int readlimit) { |
|
|
|
this.inputStream.mark(readlimit); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public boolean markSupported() { |
|
|
|
return this.inputStream.markSupported(); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public int read() throws IOException { |
|
|
|
return this.inputStream.read(); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public int read(byte[] b) throws IOException { |
|
|
|
return this.inputStream.read(b); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public int read(byte[] b, int off, int len) throws IOException { |
|
|
|
return this.inputStream.read(b, off, len); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void reset() throws IOException { |
|
|
|
this.inputStream.reset(); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public long skip(long n) throws IOException { |
|
|
|
return this.inputStream.skip(n); |
|
|
|
} |
|
|
|
|
|
|
|
static final class UnicodeBOM { |
|
|
|
private final String encoding; |
|
|
|
private final byte[] bytes; |
|
|
|
|
|
|
|
private UnicodeBOM(String encoding, byte[] bytes) { |
|
|
|
this.encoding = encoding; |
|
|
|
this.bytes = bytes; |
|
|
|
} |
|
|
|
|
|
|
|
String getEncoding() { |
|
|
|
return this.encoding; |
|
|
|
} |
|
|
|
|
|
|
|
byte[] getBytes() { |
|
|
|
return this.bytes; |
|
|
|
} |
|
|
|
} |
|
|
|
} |