AbstractJavaConverter.java

/*-
 * #%L
 * prolobjectlink-jpi
 * %%
 * Copyright (C) 2012 - 2019 Prolobjectlink Project
 * %%
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * #L%
 */
package io.github.prolobjectlink.prolog;

import static io.github.prolobjectlink.prolog.PrologTermType.ATOM_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.DOUBLE_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.FALSE_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.FLOAT_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.INTEGER_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.LIST_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.LONG_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.NIL_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.OBJECT_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.STRUCTURE_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.TRUE_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.VARIABLE_TYPE;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * Partial implementation of {@link PrologJavaConverter} interface.
 * 
 * @author Jose Zalacain
 * @since 1.0
 */
public abstract class AbstractJavaConverter implements PrologJavaConverter {

	private final PrologProvider provider;

	protected AbstractJavaConverter(PrologProvider provider) {
		this.provider = provider;
	}

	public final boolean containQuotes(String functor) {
		if (functor != null && !functor.isEmpty()) {
			return functor.startsWith("\'") && functor.endsWith("\'");
		}
		return false;
	}

	public final String removeQuotes(String functor) {
		if (containQuotes(functor)) {
			return functor.substring(1, functor.length() - 1);
		}
		return functor;
	}

	public final Object toObject(PrologTerm prologTerm) {
		if (prologTerm == null) {
			return null;
		}

		switch (prologTerm.getType()) {
		case NIL_TYPE:
			return null;
		case TRUE_TYPE:
			return true;
		case FALSE_TYPE:
			return false;
		case ATOM_TYPE:
			return removeQuotes(prologTerm.getFunctor());
		case INTEGER_TYPE:
			return ((PrologInteger) prologTerm).getIntegerValue();
		case FLOAT_TYPE:
			return ((PrologFloat) prologTerm).getFloatValue();
		case LONG_TYPE:
			return ((PrologLong) prologTerm).getLongValue();
		case DOUBLE_TYPE:
			return ((PrologDouble) prologTerm).getDoubleValue();
		case VARIABLE_TYPE:
			return prologTerm;
		case LIST_TYPE:
			return toObjectsArray(prologTerm.getArguments());
		case STRUCTURE_TYPE:
			return prologTerm;
		case OBJECT_TYPE:
			return prologTerm.getObject();
		default:
			throw new UnknownTermError(prologTerm);
		}
	}

	public final Object[] toObjectsArray(PrologTerm[] terms) {
		Object array = Array.newInstance(Object.class, terms.length);
		for (int i = 0; i < terms.length; i++) {
			Array.set(array, i, toObject(terms[i]));
		}
		return (Object[]) array;
	}

	public final PrologTerm toTerm(Object object) {
		// null pointer
		if (object == null) {
			return provider.prologNil();
		}

		// string data type
		else if (object instanceof String) {
			if (((String) object).matches("[A-Z_]*")) {
				return provider.newVariable("" + (String) object + "", 0);
			}
			return provider.newAtom("" + (String) object + "");
		}

		// primitives and wrappers data types
		else if (object.getClass() == char.class || object instanceof Character) {
			return provider.newAtom("" + (String) object + "");
		} else if (object.getClass() == byte.class || object instanceof Byte) {
			return provider.newInteger((Integer) object);
		} else if (object.getClass() == short.class || object instanceof Short) {
			return provider.newInteger((Integer) object);
		} else if (object.getClass() == boolean.class || object instanceof Boolean) {
			return (Boolean) object ? provider.prologTrue() : provider.prologFalse();
		} else if (object.getClass() == int.class || object instanceof Integer) {
			return provider.newInteger((Integer) object);
		} else if (object.getClass() == float.class || object instanceof Float) {
			return provider.newFloat((Float) object);
		} else if (object.getClass() == long.class || object instanceof Long) {
			return provider.newLong((Long) object);
		} else if (object.getClass() == double.class || object instanceof Double) {
			return provider.newDouble((Double) object);
		}

		// object array
		else if (object instanceof Object[]) {
			return provider.newList(toTermsArray((Object[]) object));
		}

		throw new UnknownTermError(object);

	}

	public final PrologTerm[] toTermsArray(Object[] objects) {
		PrologTerm[] terms = new PrologTerm[objects.length];
		for (int i = 0; i < objects.length; i++) {
			terms[i] = toTerm(objects[i]);
		}
		return terms;
	}

	public final List<Map<String, Object>> toObjectMaps(Map<String, PrologTerm>[] maps) {
		List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(maps.length);
		for (Map<String, PrologTerm> map : maps) {
			list.add(toObjectMap(map));
		}
		return list;
	}

	public final Map<String, Object> toObjectMap(Map<String, PrologTerm> map) {
		Map<String, Object> objects = new HashMap<String, Object>(map.size());
		for (Entry<String, PrologTerm> entry : map.entrySet()) {
			objects.put(entry.getKey(), toObject(entry.getValue()));
		}
		return objects;
	}

	public final List<Object> toObjectList(PrologTerm[] terms) {
		List<Object> list = new ArrayList<Object>(terms.length);
		for (PrologTerm prologTerm : terms) {
			list.add(toObject(prologTerm));
		}
		return list;
	}

	public final List<List<Object>> toObjectLists(PrologTerm[][] terms) {
		List<List<Object>> list = new ArrayList<List<Object>>(terms.length);
		for (PrologTerm[] prologTerms : terms) {
			list.add(toObjectList(prologTerms));
		}
		return list;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = super.hashCode();
		result = prime * result + ((provider == null) ? 0 : provider.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (!super.equals(obj))
			return false;
		if (getClass() != obj.getClass())
			return false;
		AbstractJavaConverter other = (AbstractJavaConverter) obj;
		if (provider == null) {
			if (other.provider != null)
				return false;
		} else if (!provider.equals(other.provider)) {
			return false;
		}
		return true;
	}

}