001: /*
002: * Copyright 2006-2007 The Scriptella Project Team.
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: */
016: package scriptella.expression;
017:
018: import scriptella.core.RuntimeIOException;
019: import scriptella.util.IOUtils;
020: import scriptella.util.StringUtils;
021:
022: import java.io.BufferedReader;
023: import java.io.Closeable;
024: import java.io.IOException;
025: import java.io.Reader;
026: import java.util.Iterator;
027: import java.util.NoSuchElementException;
028:
029: /**
030: * An Iterator over the lines in a Reader, additionally properties substitution is performed.
031: * <p>This class change the contract of {@link #hasNext()} method by
032: * throwing {@link scriptella.core.RuntimeIOException} on {@link IOException}.
033: * <p>Decorators should override {@link #format(String)} method.
034: * <p>This class is not threadsafe.
035: *
036: * @author Fyodor Kupolov
037: * @version 1.0
038: * @see PropertiesSubstitutor
039: */
040: public class LineIterator implements Iterator<String>, Closeable {
041: private PropertiesSubstitutor substitutor;
042: private BufferedReader reader;
043: private boolean trimLines;
044: private String line;
045:
046: public LineIterator(Reader reader) {
047: this (reader, null, false);
048: }
049:
050: public LineIterator(Reader reader, PropertiesSubstitutor substitutor) {
051: this (reader, substitutor, false);
052: }
053:
054: /**
055: * Constructs iterator.
056: *
057: * @param reader reader to iterate.
058: * @param substitutor substitutor to use to expand properties or null to disable substitution.
059: * @param trimLines true if the returned lines should be trimmed.
060: */
061: public LineIterator(Reader reader,
062: PropertiesSubstitutor substitutor, boolean trimLines) {
063: if (reader == null) {
064: throw new IllegalArgumentException("reader cannot be null");
065: }
066: this .reader = IOUtils.asBuffered(reader);
067: this .substitutor = substitutor;
068: this .trimLines = trimLines;
069: }
070:
071: /**
072: * @return true if a line is available for reading by {@link #next()}
073: * @throws RuntimeIOException if IO error occurs.
074: */
075: public boolean hasNext() throws RuntimeIOException {
076: if (reader == null) {
077: return false;
078: }
079: if (line == null) {
080: try {
081: line = format(reader.readLine());
082: } catch (IOException e) {
083: throw new RuntimeIOException(e);
084: }
085: }
086: return line != null;
087: }
088:
089: /**
090: * Applies additional formatting to the line read.
091: * <p>May be overriden by decorators.
092: * @param line line of text, nulls allowed.
093: * @return formatted line.
094: */
095: protected String format(String line) {
096: if (StringUtils.isEmpty(line)) {
097: return line;
098: }
099: if (trimLines) {
100: line = line.trim();
101: }
102: if (substitutor != null) {
103: line = substitutor.substitute(line);
104: }
105: return line;
106: }
107:
108: /**
109: * Returns the next avalable line in a reader.
110: *
111: * @return the next avalable line in a reader.
112: * @throws RuntimeIOException if IO error occurs.
113: * @throws NoSuchElementException if has no more elements.
114: */
115: public String next() throws RuntimeIOException,
116: NoSuchElementException {
117: if (!hasNext()) {
118: throw new NoSuchElementException();
119: }
120: final String res = line;
121: line = null;
122: return res;
123: }
124:
125: /**
126: * Skips N lines.
127: * @param n number of lines to skip.
128: * @return the actual number of lines skipped.
129: */
130: public int skip(int n) {
131: for (int i = 0; i < n; i++) {
132: if (!hasNext()) {
133: return i;
134: }
135: next();
136: }
137: return n;
138: }
139:
140: /**
141: * Returns specified line or null if EOF occured.
142: *
143: * @param n line number relative to the current line in the input. n>=0
144: * @return line n.
145: */
146: public String getLineAt(int n) {
147: skip(n);
148: return hasNext() ? next() : null;
149: }
150:
151: public void remove() throws UnsupportedOperationException {
152: throw new UnsupportedOperationException(
153: "remove not supported by " + getClass().getName());
154: }
155:
156: public void close() throws IOException {
157: if (reader != null) {
158: reader.close();
159: reader = null;
160: }
161: }
162: }
|