001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.io.input; 018 019import static org.apache.commons.io.IOUtils.EOF; 020 021import java.io.Reader; 022import java.io.Serializable; 023 024/** 025 * {@link Reader} implementation that can read from String, StringBuffer, 026 * StringBuilder or CharBuffer. 027 * <p> 028 * <strong>Note:</strong> Supports {@link #mark(int)} and {@link #reset()}. 029 * 030 * @since 1.4 031 */ 032public class CharSequenceReader extends Reader implements Serializable { 033 034 private static final long serialVersionUID = 3724187752191401220L; 035 private final CharSequence charSequence; 036 private int idx; 037 private int mark; 038 039 /** 040 * Construct a new instance with the specified character sequence. 041 * 042 * @param charSequence The character sequence, may be {@code null} 043 */ 044 public CharSequenceReader(final CharSequence charSequence) { 045 this.charSequence = charSequence != null ? charSequence : ""; 046 } 047 048 /** 049 * Close resets the file back to the start and removes any marked position. 050 */ 051 @Override 052 public void close() { 053 idx = 0; 054 mark = 0; 055 } 056 057 /** 058 * Mark the current position. 059 * 060 * @param readAheadLimit ignored 061 */ 062 @Override 063 public void mark(final int readAheadLimit) { 064 mark = idx; 065 } 066 067 /** 068 * Mark is supported (returns true). 069 * 070 * @return {@code true} 071 */ 072 @Override 073 public boolean markSupported() { 074 return true; 075 } 076 077 /** 078 * Read a single character. 079 * 080 * @return the next character from the character sequence 081 * or -1 if the end has been reached. 082 */ 083 @Override 084 public int read() { 085 if (idx >= charSequence.length()) { 086 return EOF; 087 } else { 088 return charSequence.charAt(idx++); 089 } 090 } 091 092 /** 093 * Read the specified number of characters into the array. 094 * 095 * @param array The array to store the characters in 096 * @param offset The starting position in the array to store 097 * @param length The maximum number of characters to read 098 * @return The number of characters read or -1 if there are 099 * no more 100 */ 101 @Override 102 public int read(final char[] array, final int offset, final int length) { 103 if (idx >= charSequence.length()) { 104 return EOF; 105 } 106 if (array == null) { 107 throw new NullPointerException("Character array is missing"); 108 } 109 if (length < 0 || offset < 0 || offset + length > array.length) { 110 throw new IndexOutOfBoundsException("Array Size=" + array.length + 111 ", offset=" + offset + ", length=" + length); 112 } 113 int count = 0; 114 for (int i = 0; i < length; i++) { 115 final int c = read(); 116 if (c == EOF) { 117 return count; 118 } 119 array[offset + i] = (char)c; 120 count++; 121 } 122 return count; 123 } 124 125 /** 126 * Reset the reader to the last marked position (or the beginning if 127 * mark has not been called). 128 */ 129 @Override 130 public void reset() { 131 idx = mark; 132 } 133 134 /** 135 * Skip the specified number of characters. 136 * 137 * @param n The number of characters to skip 138 * @return The actual number of characters skipped 139 */ 140 @Override 141 public long skip(final long n) { 142 if (n < 0) { 143 throw new IllegalArgumentException( 144 "Number of characters to skip is less than zero: " + n); 145 } 146 if (idx >= charSequence.length()) { 147 return EOF; 148 } 149 final int dest = (int)Math.min(charSequence.length(), idx + n); 150 final int count = dest - idx; 151 idx = dest; 152 return count; 153 } 154 155 /** 156 * Return a String representation of the underlying 157 * character sequence. 158 * 159 * @return The contents of the character sequence 160 */ 161 @Override 162 public String toString() { 163 return charSequence.toString(); 164 } 165}