/*
* Copyright 2017 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.firebase.database.connection.util;
import java.io.IOException;
import java.io.Reader;
import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.List;
public class StringListReader extends Reader {
private List<String> strings = null;
private boolean closed = false;
private int charPos;
private int stringListPos;
private int markedCharPos = charPos;
private int markedStringListPos = stringListPos;
private boolean frozen = false;
public StringListReader() {
strings = new ArrayList<>();
}
public void addString(String string) {
if (frozen) {
throw new IllegalStateException("Trying to add string after reading");
}
if (string.length() > 0) {
strings.add(string);
}
}
public void freeze() {
if (frozen) {
throw new IllegalStateException("Trying to freeze frozen StringListReader");
}
frozen = true;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
for (String string : this.strings) {
builder.append(string);
}
return builder.toString();
}
@Override
public void reset() throws IOException {
charPos = markedCharPos;
stringListPos = markedStringListPos;
}
private String currentString() {
return (stringListPos < this.strings.size()) ? this.strings.get(stringListPos) : null;
}
private int currentStringRemainingChars() {
String current = currentString();
return (current == null) ? 0 : current.length() - charPos;
}
private void checkState() throws IOException {
if (this.closed) {
throw new IOException("Stream already closed");
}
if (!frozen) {
throw new IOException("Reader needs to be frozen before read operations can be called");
}
}
private long advance(long numChars) {
long advanced = 0;
while (stringListPos < strings.size() && advanced < numChars) {
int remainingStringChars = currentStringRemainingChars();
long remainingChars = numChars - advanced;
if (remainingChars < remainingStringChars) {
charPos += remainingChars;
advanced += remainingChars;
} else {
advanced += remainingStringChars;
charPos = 0;
stringListPos++;
}
}
return advanced;
}
@Override
public int read(CharBuffer target) throws IOException {
checkState();
int remaining = target.remaining();
int total = 0;
String current = currentString();
while (remaining > 0 && current != null) {
int strLength = Math.min(current.length() - charPos, remaining);
target.put(this.strings.get(stringListPos), charPos, charPos + strLength);
remaining -= strLength;
total += strLength;
advance(strLength);
current = currentString();
}
if (total > 0 || current != null) {
return total;
} else {
return -1;
}
}
@Override
public int read() throws IOException {
checkState();
String current = currentString();
if (current == null) {
return -1;
} else {
char c = current.charAt(charPos);
advance(1);
return c;
}
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
checkState();
int charsCopied = 0;
String current = currentString();
while (current != null && charsCopied < len) {
int copyLength = Math.min(currentStringRemainingChars(), len - charsCopied);
current.getChars(charPos, charPos + copyLength, cbuf, off + charsCopied);
charsCopied += copyLength;
advance(copyLength);
current = currentString();
}
if (charsCopied > 0 || current != null) {
return charsCopied;
} else {
return -1;
}
}
@Override
public long skip(long n) throws IOException {
checkState();
return advance(n);
}
@Override
public boolean ready() throws IOException {
checkState();
return true;
}
@Override
public boolean markSupported() {
return true;
}
@Override
public void mark(int readAheadLimit) throws IOException {
checkState();
markedCharPos = charPos;
markedStringListPos = stringListPos;
}
@Override
public void close() throws IOException {
checkState();
this.closed = true;
}
}