001/* 002 * Copyright 2013 Chris Pheby 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.jadira.lang.io.io2nio; 017 018import java.io.IOException; 019import java.io.InputStream; 020import java.nio.BufferUnderflowException; 021import java.nio.ByteBuffer; 022 023/** 024 * An InputStream that reads from a ByteBuffer. This can be used to adapt the NIO type to standard Java IO. 025 */ 026public class ByteBufferBackedInputStream extends InputStream { 027 028 private boolean isClosed = false; 029 030 private ByteBuffer buf; 031 032 private int mark = -1; 033 034 private int readlimit = -1; 035 036 /** 037 * Creates a new instance for the given ByteBuffer. 038 * @param buf The ByteBuffer to use 039 */ 040 public ByteBufferBackedInputStream(ByteBuffer buf) { 041 this.buf = buf; 042 } 043 044 @Override 045 public int read() throws IOException { 046 047 if (isClosed) { 048 throw new IOException("ByteBufferInputStream was already closed"); 049 } 050 051 if (!buf.hasRemaining()) { 052 return -1; 053 } 054 try { 055 final int res = buf.get() & 0xFF; 056 invalidateMark(); 057 058 return res; 059 } catch (BufferUnderflowException e) { 060 throw new IOException("Unexpected Error reading from ByteBuffer: " + e.getMessage(), e); 061 } 062 } 063 064 private void invalidateMark() { 065 if (mark >= 0 && readlimit >= 0) { 066 if (buf.position() > (mark + readlimit)) { 067 mark = -1; 068 readlimit = -1; 069 } 070 } 071 } 072 073 @Override 074 public int read(byte[] bytes, int off, int len) throws IOException { 075 076 if (isClosed) { 077 throw new IOException("ByteBufferInputStream was already closed"); 078 } 079 080 if (!buf.hasRemaining()) { 081 return -1; 082 } 083 084 len = Math.min(len, buf.remaining()); 085 086 try { 087 buf.get(bytes, off, len); 088 } catch (BufferUnderflowException e) { 089 throw new IOException("Unexpected Error reading from ByteBuffer: " + e.getMessage(), e); 090 } catch (IndexOutOfBoundsException e) { 091 throw new IOException("Unexpected Error reading from ByteBuffer: " + e.getMessage(), e); 092 } 093 invalidateMark(); 094 return len; 095 } 096 097 @Override 098 public long skip(long n) throws IOException { 099 100 if (isClosed) { 101 throw new IOException("ByteBufferInputStream was already closed"); 102 } 103 104 long bytesSkipped = 0; 105 106 int position = buf.position(); 107 int remaining = buf.limit() - position; 108 109 int len = (int) (Math.min(n, (long) remaining)); 110 111 buf.position(position + len); 112 113 invalidateMark(); 114 return bytesSkipped; 115 } 116 117 public int available() throws IOException { 118 119 if (isClosed) { 120 throw new IOException("available() but ByteBufferInputStream was already closed"); 121 } 122 123 return buf.limit() - buf.position(); 124 }; 125 126 public void close() throws IOException { 127 isClosed = true; 128 } 129 130 public void mark(int readlimit) { 131 132 mark = buf.position(); 133 this.readlimit = readlimit; 134 } 135 136 public void rewind() throws IOException { 137 position(0); 138 } 139 140 public void position(int position) throws IOException { 141 142 if (isClosed) { 143 throw new IOException("ByteBufferInputStream was already closed"); 144 } 145 146 buf.rewind(); 147 skip(position); 148 invalidateMark(); 149 } 150 151 public void reset() throws IOException { 152 153 if (isClosed) { 154 throw new IOException("ByteBufferInputStream was already closed"); 155 } 156 157 if (mark < 0) { 158 throw new IOException("Attempted to call reset() but mark was not valid"); 159 } 160 buf.position(mark); 161 } 162 163 public boolean markSupported() { 164 return true; 165 } 166}