juzu.io.ChunkBuffer Maven / Gradle / Ivy
/*
* Copyright 2013 eXo Platform SAS
*
* 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 juzu.io;
import java.io.IOException;
import java.util.LinkedList;
/** @author Julien Viet */
public class ChunkBuffer implements Streamable, Appendable {
/** . */
private static final int STATUS_BUFFERING = 0;
/** . */
private static final int STATUS_PROVIDING = 1;
/** . */
private static final int STATUS_CLOSED = 2;
/** . */
private final LinkedList queue = new LinkedList();
/** . */
private Stream consumer = null;
/** . */
private int status = STATUS_BUFFERING;
/** . */
private final Object lock = new Object();
/** . */
private Thread.UncaughtExceptionHandler errorHandler;
public ChunkBuffer() {
}
public ChunkBuffer(Thread.UncaughtExceptionHandler errorHandler) {
this.errorHandler = errorHandler;
}
public Appendable append(CharSequence csq) throws IOException {
return append(Chunk.create(csq));
}
public Appendable append(CharSequence csq, int start, int end) throws IOException {
return append(Chunk.create(csq));
}
public Appendable append(char c) throws IOException {
return append(Chunk.create(c));
}
public ChunkBuffer append(Iterable chunks) {
for (Chunk chunk : chunks) {
append(chunk);
}
return this;
}
public ChunkBuffer append(Chunk chunk) {
synchronized (lock) {
switch (status) {
case STATUS_CLOSED:
throw new IllegalArgumentException("Already closed");
case STATUS_BUFFERING:
queue.add(chunk);
break;
case STATUS_PROVIDING:
consumer.provide(chunk);
break;
}
}
return this;
}
public void send(Stream stream) {
//
synchronized (lock) {
if (this.consumer != null) {
throw new IllegalStateException("Already consumed");
} else {
this.consumer = stream;
}
}
//
boolean close;
while (true) {
Chunk chunk;
synchronized (lock) {
if (queue.isEmpty()) {
if (status == STATUS_CLOSED) {
close = true;
} else {
status = STATUS_PROVIDING;
close = false;
}
break;
} else {
chunk = queue.removeFirst();
}
}
stream.provide(chunk);
}
//
if (close) {
stream.close(errorHandler);
}
}
public ChunkBuffer close() {
Stream stream;
synchronized (lock) {
switch (status) {
case STATUS_CLOSED:
stream = null;
break;
case STATUS_PROVIDING:
stream = this.consumer;
status = STATUS_CLOSED;
break;
case STATUS_BUFFERING:
stream = null;
status = STATUS_CLOSED;
break;
default:
throw new UnsupportedOperationException();
}
}
if (stream != null) {
stream.close(errorHandler);
}
return this;
}
}