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 * @version $Id: CharSequenceReader.java 1642757 2014-12-01 21:09:30Z sebb $ 031 * @since 1.4 032 */ 033public class CharSequenceReader extends Reader implements Serializable { 034 035 private static final long serialVersionUID = 3724187752191401220L; 036 private final CharSequence charSequence; 037 private int idx; 038 private int mark; 039 040 /** 041 * Construct a new instance with the specified character sequence. 042 * 043 * @param charSequence The character sequence, may be {@code null} 044 */ 045 public CharSequenceReader(final CharSequence charSequence) { 046 this.charSequence = charSequence != null ? charSequence : ""; 047 } 048 049 /** 050 * Close resets the file back to the start and removes any marked position. 051 */ 052 @Override 053 public void close() { 054 idx = 0; 055 mark = 0; 056 } 057 058 /** 059 * Mark the current position. 060 * 061 * @param readAheadLimit ignored 062 */ 063 @Override 064 public void mark(final int readAheadLimit) { 065 mark = idx; 066 } 067 068 /** 069 * Mark is supported (returns true). 070 * 071 * @return {@code true} 072 */ 073 @Override 074 public boolean markSupported() { 075 return true; 076 } 077 078 /** 079 * Read a single character. 080 * 081 * @return the next character from the character sequence 082 * or -1 if the end has been reached. 083 */ 084 @Override 085 public int read() { 086 if (idx >= charSequence.length()) { 087 return EOF; 088 } else { 089 return charSequence.charAt(idx++); 090 } 091 } 092 093 /** 094 * Read the sepcified number of characters into the array. 095 * 096 * @param array The array to store the characters in 097 * @param offset The starting position in the array to store 098 * @param length The maximum number of characters to read 099 * @return The number of characters read or -1 if there are 100 * no more 101 */ 102 @Override 103 public int read(final char[] array, final int offset, final int length) { 104 if (idx >= charSequence.length()) { 105 return EOF; 106 } 107 if (array == null) { 108 throw new NullPointerException("Character array is missing"); 109 } 110 if (length < 0 || offset < 0 || offset + length > array.length) { 111 throw new IndexOutOfBoundsException("Array Size=" + array.length + 112 ", offset=" + offset + ", length=" + length); 113 } 114 int count = 0; 115 for (int i = 0; i < length; i++) { 116 final int c = read(); 117 if (c == EOF) { 118 return count; 119 } 120 array[offset + i] = (char)c; 121 count++; 122 } 123 return count; 124 } 125 126 /** 127 * Reset the reader to the last marked position (or the beginning if 128 * mark has not been called). 129 */ 130 @Override 131 public void reset() { 132 idx = mark; 133 } 134 135 /** 136 * Skip the specified number of characters. 137 * 138 * @param n The number of characters to skip 139 * @return The actual number of characters skipped 140 */ 141 @Override 142 public long skip(final long n) { 143 if (n < 0) { 144 throw new IllegalArgumentException( 145 "Number of characters to skip is less than zero: " + n); 146 } 147 if (idx >= charSequence.length()) { 148 return EOF; 149 } 150 final int dest = (int)Math.min(charSequence.length(), idx + n); 151 final int count = dest - idx; 152 idx = dest; 153 return count; 154 } 155 156 /** 157 * Return a String representation of the underlying 158 * character sequence. 159 * 160 * @return The contents of the character sequence 161 */ 162 @Override 163 public String toString() { 164 return charSequence.toString(); 165 } 166}