IOLibrary.java

/*
 * #%L
 * prolobjectlink-jpi-jtrolog
 * %%
 * Copyright (C) 2012 - 2018 WorkLogic Project
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 2.1 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
 * #L%
 */
/*
 * tuProlog - Copyright (C) 2001-2002  aliCE team at deis.unibo.it
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package jTrolog.lib;

import jTrolog.engine.*;
import jTrolog.terms.*;
import jTrolog.terms.Double;
import jTrolog.terms.Number;
import jTrolog.parser.Parser;

import java.io.*;
import java.util.HashMap;
import java.util.Random;

/**
 * This class provides basic I/O predicates.
 * 
 * @author ivar.orstavik@hist.no
 */
@SuppressWarnings({ "rawtypes", "unchecked", "serial" })
public class IOLibrary extends Library {

	private HashMap streams;

	private String in;
	private String out;

	private Random gen = new Random();

	public IOLibrary() {
		streams = new HashMap();
		in = "stdin";
		out = "stdout";
	}

	public boolean see_1(BindingsTable bt, Term arg0) throws Exception {
		if (!BasicLibrary.atom_1(bt, arg0))
			return false;

		String requested = ((Struct) arg0).name;
		// 1. close already open stream if seeing a new input, then rename
		// current in
		if (!requested.equals(in)) {
			InputStream inputStream = getInputStream(in);
			if (inputStream != null)
				inputStream.close();
			in = requested;
		}

		// 2. if the requested stream is not already open, then open it
		InputStream newIn = getInputStream(in);
		if (newIn == null) {
			newIn = new FileInputStream(in);
			streams.put(in, newIn);
		}
		return true;
	}

	public boolean seen_0(BindingsTable bt) throws Exception {
		InputStream inputStream = getInputStream(in);
		if (inputStream != null)
			inputStream.close();
		in = "stdin";
		return true;
	}

	public boolean seeing_1(BindingsTable bt, Term t) {
		return bt.unify(t, new StructAtom(in));
	}

	public boolean tell_1(BindingsTable bt, Term arg0) throws Exception {
		if (!BasicLibrary.atom_1(bt, arg0))
			return false;

		String requested = ((Struct) arg0).name;
		// 1. close already open stream if seeing a new input, then rename
		// current in
		if (!requested.equals(out)) {
			OutputStream inputStream = getOutputStream(out);
			if (inputStream != null)
				inputStream.close();
			out = requested;
		}

		// 2. if the requested stream is not already open, then open it
		OutputStream newOut = getOutputStream(out);
		if (newOut == null) {
			newOut = new FileOutputStream(out);
			streams.put(out, newOut);
		}
		return true;
	}

	public boolean told_0(BindingsTable bt) throws Exception {
		OutputStream outputStream = getOutputStream(out);
		if (outputStream != null)
			outputStream.close();
		out = "stdout";
		return true;
	}

	public boolean telling_1(BindingsTable bt, Term arg0) {
		return bt.unify(arg0, new StructAtom(out));
	}

	public boolean put_1(BindingsTable bt, Term arg0) throws Exception {
		if (!BasicLibrary.atom_1(bt, arg0))
			return false;

		String ch = ((Struct) arg0).name;
		if (ch.length() > 1)
			return false;
		getOutputStream(out).write((byte) ch.charAt(0));
		return true;
	}

	public boolean get0_1(BindingsTable bt, Term arg0) throws Exception {
		int ch = getInputStream(in).read();
		Term a1 = ch == -1 ? (Term) new Int(-1) : (Term) new StructAtom(Character.toString((char) ch));
		return bt.unify(arg0, a1);
	}

	public boolean get_1(BindingsTable bt, Term arg0) throws Exception {
		InputStream inputStream = getInputStream(in);
		int ch;
		do {
			ch = inputStream.read();
		} while (ch < 0x20 && ch >= 0);
		Term a1 = ch == -1 ? (Term) new Int(-1) : (Term) new StructAtom(Character.toString((char) ch));
		return bt.unify(arg0, a1);
	}

	public boolean tab_1(BindingsTable bt, Int arg) throws Exception {
		int n = arg.intValue();
		OutputStream outputStream = getOutputStream(out);
		for (int i = 0; i < n; i++)
			outputStream.write(0x20);
		return true;
	}

	public boolean read_1(BindingsTable bt, Term arg0) throws Exception {
		InputStream inputStream = getInputStream(in);
		StringBuffer res = new StringBuffer();
		boolean single_quotes = false;
		boolean double_quotes = false;
		do {
			int ch = inputStream.read();
			if (ch == -1)
				break;
			else if (ch == '\'')
				single_quotes = !single_quotes;
			else if (ch == '\"')
				double_quotes = !double_quotes;
			else if (ch == '.' && !(single_quotes || double_quotes))
				break;
			res.append((char) ch);
		} while (true);
		Term result = new Parser(res.toString(), ((Library) this).engine).nextTerm(false);
		return bt.unify(arg0, result);
	}

	public boolean write_1(BindingsTable bt, Term arg0) throws Exception {
		OutputStream outputStream = getOutputStream(out);
		outputStream.write(arg0.toString().getBytes());
		return true;
	}

	public boolean print_1(BindingsTable bt, Term arg0) throws Exception {
		OutputStream outputStream = getOutputStream(out);
		outputStream.write(Parser.removeApices(arg0.toString()).getBytes());
		return true;
	}

	public boolean nl_0(BindingsTable bt) throws Exception {
		OutputStream outputStream = getOutputStream(out);
		outputStream.write('\n');
		return true;
	}

	/**
	 * reads a source text from a file. It's useful used with agent predicate:
	 * text_from_file(File,Source), agent(Source).
	 */
	public boolean text_from_file_2(BindingsTable bt, Term file_name, Term text) throws IOException {
		StringBuffer res = new StringBuffer();
		BufferedReader in;
		Object possibleInput = streams.get(Parser.removeApices(file_name.toString()));
		if (possibleInput instanceof InputStream) {
			in = new BufferedReader(new InputStreamReader((InputStream) possibleInput));
		} else {
			in = new BufferedReader(new FileReader("infilename"));
		}
		String str;
		while ((str = in.readLine()) != null)
			res.append(str);
		in.close();

		return bt.unify(text, new StructAtom(res.toString()));
	}

	// miscellanea

	public boolean rand_float_1(BindingsTable bt, Term t) {
		return bt.unify(t, new Double(gen.nextFloat()));
	}

	public boolean rand_int_2(BindingsTable bt, Number argNum, Term num) {
		return bt.unify(num, new Int(gen.nextInt(argNum.intValue())));
	}

	public String getTheory() {
		return "consult(File) :- text_from_file(File,Text), add_theory(Text).\n"
				+ "reconsult(File) :- text_from_file(File,Text), set_theory(Text).\n"
				+ "solve_file(File,Goal) :- text_from_file(File,Text),text_term(Text,Goal),call(Goal).\n";
	}

	public static String readStream(InputStream is) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(is));
		StringBuilder sb = new StringBuilder();
		String line;
		while ((line = br.readLine()) != null)
			sb.append(line).append("\n");
		br.close();
		return sb.toString();
	}

	private OutputStream getOutputStream(String out) {
		if ("stdout".equals(out))
			return ((Library) this).engine.getPrintStream();
		return (OutputStream) streams.get(out);
	}

	private InputStream getInputStream(String in) {
		if ("stdout".equals(in))
			return System.in;
		return (InputStream) streams.get(in);
	}
}