/* * Copyright 2000-2013 Enonic AS * http://www.enonic.com/license */ package com.enonic.cms.framework.io; import java.io.IOException; import java.io.InputStream; import java.io.PushbackInputStream; /** * This is an input stream that is unicode BOM (byte order mark) aware. */ public class UnicodeInputStream extends InputStream { /** * The maximum amount of bytes to read for a BOM */ private static final int MAX_BOM_SIZE = 4; /** * True if the BOM itself should be skipped and not read. */ private final boolean skipBOM; /** * Pushback input stream. */ private final PushbackInputStream in; /** * Byte order mark. */ private ByteOrderMark bom; /** * Bom buffer. */ private final byte[] bomBuffer = new byte[MAX_BOM_SIZE]; /** * Bom position. */ private int bomPos = 0; /** * Construct the stream. */ public UnicodeInputStream( InputStream in ) throws IOException { this( in, true ); } /** * Construct the stream. */ public UnicodeInputStream( InputStream in, boolean skipBOM ) throws IOException { if ( in == null ) { throw new IllegalArgumentException( "InputStream is null" ); } this.in = new PushbackInputStream( in, MAX_BOM_SIZE ); this.skipBOM = skipBOM; this.bom = readBom(); } /** * Return the encoding from stream based on BOM. */ public String getEncoding() { return this.bom != null ? this.bom.getEncoding() : null; } /** * Return the byte order mark. */ public ByteOrderMark getByteOrderMark() { return this.bom; } /** * Read bom byte. */ private boolean readBomByte() throws IOException { if ( this.bomPos >= this.bomBuffer.length ) { return false; } int res = this.in.read(); if ( res == -1 ) { return false; } this.bomBuffer[this.bomPos++] = (byte) res; return true; } /** * Matches bom. */ private ByteOrderMark matchBom() throws IOException { while ( readBomByte() ) { ByteOrderMark bom = ByteOrderMark.resolve( this.bomBuffer ); if ( bom != null ) { return bom; } } return null; } /** * Pushback bom. */ private void pushbackBom( ByteOrderMark bom ) throws IOException { int count = this.bomPos; int start = 0; if ( ( bom != null ) && this.skipBOM ) { start = bom.getBytes().length; count = ( this.bomPos - start ); if ( count < 0 ) { throw new IOException( "Match has more bytes than available!" ); } } this.in.unread( this.bomBuffer, start, count ); } /** * Read the bom. */ private ByteOrderMark readBom() throws IOException { this.bomPos = 0; ByteOrderMark bom = matchBom(); pushbackBom( bom ); return bom; } public int read() throws IOException { return in.read(); } public void close() throws IOException { in.close(); } public void reset() throws IOException { in.reset(); } public int available() throws IOException { return in.available(); } public void mark( int i ) { in.mark( i ); } public boolean markSupported() { return in.markSupported(); } public int read( byte[] bytes ) throws IOException { return in.read( bytes ); } public int read( byte[] bytes, int i, int i1 ) throws IOException { return in.read( bytes, i, i1 ); } public long skip( long l ) throws IOException { return in.skip( l ); } }