JLogUtil.java

/*
 * #%L
 * prolobjectlink-jpi-jlog
 * %%
 * Copyright (C) 2019 Prolobjectlink Project
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 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 Public License for more details.
 * 
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #L%
 */
package io.github.prolobjectlink.prolog.jlog;

import static io.github.prolobjectlink.prolog.AbstractConverter.SIMPLE_ATOM_REGEX;
import static io.github.prolobjectlink.prolog.jlog.JLog.FUNCTORS;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Enumeration;
import java.util.Map.Entry;

import io.github.prolobjectlink.prolog.PrologProvider;
import io.github.prolobjectlink.prolog.PrologTerm;
import io.github.prolobjectlink.prolog.StructureExpectedError;
import ubc.cs.JLog.Foundation.jKnowledgeBase;
import ubc.cs.JLog.Foundation.jPrologServices;
import ubc.cs.JLog.Foundation.jRule;
import ubc.cs.JLog.Foundation.jRuleDefinitions;
import ubc.cs.JLog.Parser.pOperatorRegistry;
import ubc.cs.JLog.Parser.pParseStream;
import ubc.cs.JLog.Parser.pPredicateRegistry;
import ubc.cs.JLog.Terms.jAtom;
import ubc.cs.JLog.Terms.jBuiltinRule;
import ubc.cs.JLog.Terms.jCompoundTerm;
import ubc.cs.JLog.Terms.jCons;
import ubc.cs.JLog.Terms.jIf;
import ubc.cs.JLog.Terms.jPredicate;
import ubc.cs.JLog.Terms.jPredicateTerms;
import ubc.cs.JLog.Terms.jTerm;
import ubc.cs.JLog.Terms.jVariable;

/**
 * Util class for {@link JLogTerm}
 * 
 * @author Jose Zalacain
 * @since 1.0
 *
 */
final class JLogUtil {

	private JLogUtil() {
	}

	static final PrologTerm toTerm(PrologProvider provider, Object object) {

		// null pointer
		if (object == null) {
			return new JLogNil(provider);
		}

		// string data type
		else if (object instanceof String) {
			String string = (String) object;
			int index = string.indexOf('(');
			if (index > -1) {
				String functor = string.substring(0, index);
				String arguments = string.substring(index);
				if (!functor.matches(SIMPLE_ATOM_REGEX)) {
					StringBuilder buffer = new StringBuilder();
					buffer.append('\'');
					buffer.append(functor);
					buffer.append('\'');
					String quoted = "" + buffer + "";
					buffer.append(arguments);
					string = "" + buffer + "";
					FUNCTORS.put(functor, quoted);

					// jlog need treatment for complex functors
					for (Entry<String, String> entry : FUNCTORS.entrySet()) {

						// retrieve cached functors
						String key = entry.getKey();
						String value = entry.getValue();

						// first and unique term pattern
						String firstRegex = "(" + key + "";
						if (string.contains(firstRegex)) {
							string = string.replaceAll(key, value);
						}

						// non-first term pattern
						String nonFirstRegex = "," + key + "";
						if (string.contains(nonFirstRegex)) {
							string = string.replaceAll(key, value);
						}

					}
				}
			}

			return provider.parseTerm(string);
		}

		// primitives and wrappers data types
		else if (object.getClass() == boolean.class || object instanceof Boolean) {
			return (Boolean) object ? new JLogTrue(provider) : new JLogFalse(provider);
		} else if (object.getClass() == int.class || object instanceof Integer) {
			return new JLogInteger(provider, (Integer) object);
		} else if (object.getClass() == float.class || object instanceof Float) {
			return new JLogFloat(provider, (Float) object);
		} else if (object.getClass() == long.class || object instanceof Long) {
			return new JLogLong(provider, (Long) object);
		} else if (object.getClass() == double.class || object instanceof Double) {
			return new JLogDouble(provider, (Double) object);
		}

		//
		else if (object instanceof Object[]) {
			Object[] objects = (Object[]) object;
			PrologTerm[] terms = new PrologTerm[objects.length];
			for (int i = 0; i < objects.length; i++) {
				terms[i] = toTerm(provider, objects[i]);
			}
			return new JLogList(provider, terms);
		} else if (object instanceof jTerm) {
			return toTerm(provider, (jTerm) object);
		}
		return null;
	}

	static final jRule toRule(String str, jPrologServices engine) {
		jPredicateTerms emptyBody = new jPredicateTerms();
		jKnowledgeBase ikb = engine.getKnowledgeBase();
		pOperatorRegistry ior = engine.getOperatorRegistry();
		pPredicateRegistry ipr = engine.getPredicateRegistry();
		String clause = str.charAt(str.length() - 1) == '.' ? str : str + '.';
		jTerm term = new pParseStream(clause, ikb, ipr, ior).parseTerm();
		if (term.type == jTerm.TYPE_PREDICATE) { // fact
			jPredicate predicate = (jPredicate) term;
			return new jRule(predicate, emptyBody);
		} else if (term.type == jTerm.TYPE_ATOM) {
			jAtom atom = (jAtom) term;
			jPredicate predicate = new jPredicate(atom);
			return new jRule(predicate, emptyBody);
		} else if (term.type == jTerm.TYPE_IF) { // rule
			jIf rule = (jIf) term;
			jPredicate h = (jPredicate) rule.getLHS();
			jTerm ruleBody = rule.getRHS();
			switch (ruleBody.type) {

			// only and just only predicate
			case jTerm.TYPE_PREDICATE:
				jPredicate predicateBody = (jPredicate) ruleBody;
				jPredicateTerms b = new jPredicateTerms();
				b.addTerm(predicateBody);
				return new jRule(h, b);

			//
			case jTerm.TYPE_CONS:
				b = new jPredicateTerms();
				while (ruleBody instanceof jCons) {
					b.addTerm(((jCons) ruleBody).getLHS());
					ruleBody = ((jCons) ruleBody).getRHS().getTerm();
				}
				b.addTerm(ruleBody);
				return new jRule(h, b);

			case jTerm.TYPE_ATOM:
				jPredicateTerms atom = new jPredicateTerms();
				atom.addTerm(ruleBody);
				return new jRule(h, atom);

			// cut type
			case jTerm.TYPE_BUILTINPREDICATE:
				jPredicateTerms builtin = new jPredicateTerms();
				builtin.addTerm(ruleBody);
				return new jRule(h, builtin);

			//
			default:
				throw new StructureExpectedError(term);
			}

		}

		// no rule
		throw new StructureExpectedError(term);

	}

	static final jRule toRule(PrologProvider provider, PrologTerm head, PrologTerm... body) {
		jTerm termHead = provider.fromTerm(head, jTerm.class);
		if (termHead.type == jTerm.TYPE_PREDICATE || termHead.type == jTerm.TYPE_ATOM) {
			jPredicate predicateHead = null;
			if (termHead.type == jTerm.TYPE_PREDICATE) {
				predicateHead = (jPredicate) termHead;
			} else if (termHead.type == jTerm.TYPE_ATOM) {
				jAtom atomHead = (jAtom) termHead;
				predicateHead = new jPredicate(atomHead);
			}
			jPredicateTerms predicateBody = new jPredicateTerms();
			for (PrologTerm iPrologTerm : body) {
				predicateBody.addTerm(provider.fromTerm(iPrologTerm, jTerm.class));
			}
			return new jRule(predicateHead, predicateBody);
		}
		throw new StructureExpectedError(head);
	}

	static final String toString(jPrologServices engine) {
		jKnowledgeBase kb = engine.getKnowledgeBase();
		StringWriter stringWriter = new StringWriter();
		PrintWriter writer = new PrintWriter(stringWriter);
		Enumeration<?> enumeration = kb.enumDefinitions();
		while (enumeration.hasMoreElements()) {
			jRuleDefinitions object = (jRuleDefinitions) enumeration.nextElement();
			Enumeration<?> r = object.enumRules();
			while (r.hasMoreElements()) {
				Object object2 = r.nextElement();
				if (!(object2 instanceof jBuiltinRule)) {
					jRule jRule = (jRule) object2;

					// rule head
					jPredicate ruleHead = jRule.getHead();
					jCompoundTerm args = ruleHead.getArguments();
					String functor = ruleHead.getName();
					String ok = toString(functor, args);
					writer.print(ok);

					// rule body
					jPredicateTerms ruleBody = jRule.getBase();
					Enumeration<?> k = ruleBody.enumTerms();
					if (k.hasMoreElements()) {
						writer.print(":-");
						while (k.hasMoreElements()) {
							jTerm term = (jTerm) k.nextElement();
							if (term instanceof jPredicate) {
								jPredicate p = (jPredicate) term;
								args = p.getArguments();
								functor = p.getName();
								ok = toString(functor, args);
								writer.print(ok);
							} else {
								writer.print(term);
							}
							if (k.hasMoreElements()) {
								writer.print(',');
							}
						}
					}

					// rule end
					writer.println('.');
				}
			}
		}
		writer.flush();
		writer.close();

		return "" + stringWriter + "";
	}

	private static final String toString(String functor, jCompoundTerm args) {
		StringBuilder buffer = new StringBuilder();
		if (!functor.startsWith("'") && !functor.endsWith("'") && !functor.matches(SIMPLE_ATOM_REGEX)) {
			buffer.append('\'');
			buffer.append(functor);
			buffer.append('\'');
			String quoted = "" + buffer + "";
			FUNCTORS.put(functor, quoted);
		} else {
			buffer.append(functor);
		}
		Enumeration<?> e = args.enumTerms();
		if (e.hasMoreElements()) {
			buffer.append('(');
			while (e.hasMoreElements()) {
				jTerm jTerm = (jTerm) e.nextElement();
				if (jTerm instanceof jVariable) {
					jVariable jVar = (jVariable) jTerm;
					buffer.append(jVar.getName());
				} else if (jTerm instanceof jPredicate) {
					jPredicate p = (jPredicate) jTerm;
					String arg = toString(p.getName(), p.getArguments());
					buffer.append(arg);
				} else {
					buffer.append(jTerm);
				}
				if (e.hasMoreElements())
					buffer.append(',');
			}
			buffer.append(')');
		}
		return "" + buffer + "";
	}

	static final String removeQuotesIfNeed(String functor) {
		if (functor.startsWith("'") && functor.endsWith("'")) {
			return functor.substring(1, functor.length() - 1);
		}
		return functor;
	}

	static final String rectify(String str) {
		for (Entry<String, String> entry : FUNCTORS.entrySet()) {

			// retrieve cached functors
			String key = entry.getKey();
			String value = entry.getValue();

			// first and unique term pattern
			String firstRegex = "(" + key + "";
			if (str.contains(firstRegex)) {
				str = str.replaceAll(key, value);
			}

			// non-first term pattern
			String nonFirstRegex = "," + key + "";
			if (str.contains(nonFirstRegex)) {
				str = str.replaceAll(key, value);
			}

		}
		return str;
	}

	private static final jPredicate retrieve(jPredicate predicate) {
		int arity = predicate.getArity();
		String functor = predicate.getName();
		if (functor.startsWith("'") && functor.endsWith("'")) {
			String nFunctor = functor.substring(1, functor.length() - 1);
			jCompoundTerm args = predicate.getArguments();
			jCompoundTerm nArgs = new jCompoundTerm(arity);
			for (int i = 0; i < args.size(); i++) {
				jTerm term = args.elementAt(i);
				if (term instanceof jPredicate) {
					jPredicate p = (jPredicate) term;
					nArgs.addTerm(retrieve(p));
				}
			}
			predicate = new jPredicate(nFunctor, nArgs);
		}
		return predicate;
	}

}