/* * Copyright 2014 Igor Maznitsa (http://www.igormaznitsa.com). * * 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.igormaznitsa.prol.io; import com.igormaznitsa.prol.data.Term; import com.igormaznitsa.prol.data.TermInteger; import com.igormaznitsa.prol.logic.ProlContext; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PipedReader; import java.io.PipedWriter; import java.nio.charset.Charset; /** * The class describes a memory pipe which is the object allows input-output * operations in memory buffer. It is based on the java pipe mechanism so you * should remember that read operations can be block execution until they get * new data, to avoid that case don't forget to call 'told/0' predicate for a * pipe for active nenory pipe. If you try read data of the pipe from Java then * don't please to call closeForWrittersOnly() method to avoid any blocking for * read operations. Also the pipe data can be read only one time because it is a * temporary storage. Once you call the 'seen/0' predicate for an active pipe,it * will be removed from the inside pool! * * @author Igor Maznitsa (igor.maznitsa@igormaznitsa.com) * @see com.igormaznitsa.prol.io.ProlStream * @see com.igormaznitsa.prol.io.ProlTextWriter * @see com.igormaznitsa.prol.io.ProlTextReader */ public class ProlMemoryPipe implements ProlStream, ProlTextReader, ProlTextWriter { /** * The variable saves the resource ID */ private final String resourceId; /** * The context is using the pipe */ private final ProlContext context; /** * The reader used by the pipe to read from itself */ private final ProlTextInputStream reader; /** * The witer used by the pipe to write into itself */ private final ProlTextOutputStream writer; /** * The constructor * * @param resourceId the resource identifier, must not be null * @param context the owner context for the pipe, must not be null * @throws IOException it will be thrown if there will be any transport errors */ public ProlMemoryPipe(final String resourceId, final ProlContext context) throws IOException { if (resourceId == null) { throw new IllegalArgumentException("Resource identifier must not be null"); } if (context == null) { throw new IllegalArgumentException("Context must not be null"); } this.resourceId = resourceId; this.context = context; final PipedWriter pipeWriter = new PipedWriter(); final PipedReader pipeReader = new PipedReader(pipeWriter); reader = new ProlTextInputStream(pipeReader, context); writer = new ProlTextOutputStream(pipeWriter, context, false); } /** * Get the reader for the pipe * * @return the pipe reader */ public ProlTextReader getReader() { return reader; } /** * Get the reader for the pipe * * @return the pipe reader */ public ProlTextWriter getWriter() { return writer; } @Override public ProlContext getContext() { return context; } @Override public void close() throws IOException { synchronized (writer) { writer.close(); } synchronized (reader) { reader.close(); } } /** * Close only for output data, it will not be possible to write data into the * pipe after the operation. From prolog the method will be called when told/0 * is called for the pipe. If you want to manipulate with the content of the * pipe, you have to call the method else your read operations can be blocked * until incomming data. * * @throws IOException it will be thrown if there is any exception */ public void closeForWriteOnly() throws IOException { synchronized (writer) { writer.close(); } } @Override public String getResourceId() { return resourceId; } @Override public Term getAsTerm() { final Term result = new Term(resourceId); return result; } @Override public Term readToken() throws IOException { synchronized (reader) { return reader.readToken(); } } @Override public Term readTerm() throws IOException { synchronized (reader) { return reader.readTerm(); } } @Override public TermInteger readChar() throws IOException { synchronized (reader) { return reader.readChar(); } } @Override public void writeTerm(Term term) throws IOException { synchronized (writer) { writer.writeTerm(term); } } @Override public void writeChar(Term term) throws IOException { synchronized (writer) { writer.writeChar(term); } } /** * Get the content written into the pipe as a String object. Remember that the * operation will clear the buffer. * * @param charset the charset which will be used to convert byte data from the * pipe into the String * @return the pipe data content as a String object * @throws IOException it will be thrown if there is any IO error during the * operation */ public String getAllDataAsString(final Charset charset) throws IOException { return new String(getAllDataAsByteArray(), charset); } /** * Get the content written into the pipe as a byte array * * @return the data from the pipe as a byte array * @throws IOException it will be thrown if there is any IO operation */ public byte[] getAllDataAsByteArray() throws IOException { synchronized (reader) { final ByteArrayOutputStream buffer = new ByteArrayOutputStream(1024); while (true) { final int chr = reader.readChar().getNumericValue().intValue(); if (chr < 0) { break; } buffer.write(chr); } return buffer.toByteArray(); } } }