/*
* 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 alice.tuprolog.lib;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import alice.tuprolog.Int;
import alice.tuprolog.Library;
import alice.tuprolog.Number;
import alice.tuprolog.Predicate;
import alice.tuprolog.Struct;
import alice.tuprolog.Term;
import alice.tuprolog.Var;
/**
* This class represents a tuProlog library enabling the interaction
* with the Java environment from tuProlog.
*
* Works only with JDK 1.2 (because of setAccessible method)
*
* The most specific method algorithm used to find constructors / methods
* has been inspired by the article
* "What Is Interactive Scripting?", by Michael Travers
* Dr. Dobb's -- Software Tools for the Professional Programmer
* January 2000
* CMP Media Inc., a United News and Media Company
*
* Library/Theory Dependency: BasicLibrary
*/
public class JavaLibrary extends Library {
/**
* java objects referenced by prolog terms (keys)
*/
private HashMap<String, Object> currentObjects = new HashMap<String, Object>();
/**
* inverse map useful for implementation issue
*/
private IdentityHashMap<Object, Struct> currentObjectsInverse = new IdentityHashMap<Object, Struct>();
private HashMap<String, Object> staticObjects = new HashMap<String, Object>();
private IdentityHashMap<Object, Struct> staticObjectsInverse = new IdentityHashMap<Object, Struct>();
/** progressive counter used to identify registered objects */
private int id = 0;
@Override
public String getTheory() {
return
//
// operators defined by the JavaLibrary theory
//
":- op(800,xfx,'<-').\n" +
":- op(850,xfx,'returns').\n" +
":- op(200,xfx,'as').\n" +
":- op(600,xfx,'.'). \n" +
//
// flags defined by the JavaLibrary theory
//
//":- flag(java_object_backtrackable,[true,false],false,true).\n" +
//
//
//"java_object(ClassName,Args,Id):- current_prolog_flag(java_object_backtrackable,false),!,java_object_nb(ClassName,Args,Id).\n" +
//"java_object(ClassName,Args,Id):- !,java_object_bt(ClassName,Args,Id).\n" +
"java_object_bt(ClassName,Args,Id):- java_object(ClassName,Args,Id).\n" +
"java_object_bt(ClassName,Args,Id):- destroy_object(Id).\n" +
"Obj <- What :- java_call(Obj,What,Res), Res \\== false.\n" +
"Obj <- What returns Res :- java_call(Obj,What,Res).\n" +
"java_array_set(Array,Index,Object):- class('java.lang.reflect.Array') <- set(Array as 'java.lang.Object',Index,Object as 'java.lang.Object'),!.\n" +
"java_array_set(Array,Index,Object):- java_array_set_primitive(Array,Index,Object).\n"+
"java_array_get(Array,Index,Object):- class('java.lang.reflect.Array') <- get(Array as 'java.lang.Object',Index) returns Object,!.\n" +
"java_array_get(Array,Index,Object):- java_array_get_primitive(Array,Index,Object).\n"+
"java_array_length(Array,Length):- class('java.lang.reflect.Array') <- getLength(Array as 'java.lang.Object') returns Length.\n" +
"java_object_string(Object,String):- Object <- toString returns String. \n";
}
@Override
public void dismiss() {
currentObjects.clear();
currentObjectsInverse.clear();
}
public void dismissAll() {
currentObjects.clear();
currentObjectsInverse.clear();
staticObjects.clear();
staticObjectsInverse.clear();
}
@Override
public void onSolveBegin(Term goal) {
//id = 0;
currentObjects.clear();
currentObjectsInverse.clear();
for (Map.Entry<Object, Struct> en : staticObjectsInverse.entrySet())
bindDynamicObject(en.getValue(), en.getKey());
preregisterObjects();
}
/**
* objects actually pre-registered in order to be
* available since the beginning of demonstration
*/
protected void preregisterObjects() {
try {
bindDynamicObject(new Struct("stdout"), System.out);
bindDynamicObject(new Struct("stderr"), System.err);
bindDynamicObject(new Struct("runtime"), Runtime.getRuntime());
bindDynamicObject(new Struct("current_thread"), Thread.currentThread());
} catch (Exception ex) {
ex.printStackTrace();
}
}
//----------------------------------------------------------------------------
/**
* Creates of a java object - not backtrackable case
*/
@Predicate("java_object/3")
public boolean javaObject(Term className, Term argl, Term id) {
className = className.getTerm();
Struct arg = (Struct) argl.getTerm();
id = id.getTerm();
try {
if (!className.isAtom()) {
return false;
}
String clName = ((Struct) className).getName();
// check for array type
if (clName.endsWith("[]")) {
Object[] list = getArrayFromList(arg);
int nargs = ((Number) list[0]).intValue();
return javaArray(clName, nargs, id);
}
Signature args = parseArg(getArrayFromList(arg));
if (args == null) {
return false;
}
// object creation with argument described in arguments
try {
Class<?> cl = Class.forName(clName);
Object[] args_value = args.getValues();
//
Constructor<?> co = lookupConstructor(cl, args.getTypes(), args_value);
//
if (co == null) {
getEngine().warn("Constructor not found: class " + clName);
return false;
}
Object obj = co.newInstance(args_value);
return bindDynamicObject(id, obj);
} catch (ClassNotFoundException ex) {
getEngine().warn("Java class not found: " + clName);
return false;
} catch (InvocationTargetException ex) {
getEngine().warn("Invalid constructor arguments.");
return false;
} catch (NoSuchMethodException ex) {
getEngine().warn("Constructor not found: " + args.getTypes());
return false;
} catch (InstantiationException ex) {
getEngine().warn("Objects of class " + clName + " cannot be instantiated");
return false;
} catch (IllegalArgumentException ex) {
getEngine().warn("Illegal constructor arguments " + args);
return false;
}
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
}
/**
* Destroy the link to a java object - called not directly, but from
* predicate java_object (as second choice, for backtracking)
*/
@Predicate("destroy_object/1")
public boolean destroyObject(Term id) {
id = id.getTerm();
try {
if (id.isGround()) {
unregisterDynamic((Struct)id);
}
return true;
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
}
/**
* Creates a Java class.
*/
@Predicate("java_class/4")
public boolean javaClass(Term clSource, Term clName, Term clPathes, Term id) {
Struct classSource = (Struct) clSource.getTerm();
Struct className = (Struct) clName.getTerm();
Struct classPathes = (Struct) clPathes.getTerm();
id = id.getTerm();
try {
String fullClassName = className.toStringWithoutApices();
String fullClassPath = fullClassName.replace('.', '/');
String cp = "";
for (Term t : classPathes) {
if (cp.length() > 0)
cp += ";";
cp += ((Struct) t).toStringWithoutApices();
}
if (cp.length() > 0) {
cp = " -classpath " + cp;
}
String text = classSource.toStringWithoutApices();
//System.out.println("class source: "+text+
// "\nid: "+id+
// "\npath: "+fullClassPath);
try {
FileWriter file = new FileWriter(fullClassPath + ".java");
file.write(text);
file.close();
} catch (IOException ex) {
getEngine().warn("Compilation of java sources failed");
getEngine().warn("(creation of " + fullClassPath + ".java fail failed)");
return false;
}
String cmd = "javac " + cp + " " + fullClassPath + ".java";
//System.out.println("EXEC: "+cmd);
try {
Process jc = Runtime.getRuntime().exec(cmd);
int res = jc.waitFor();
if (res != 0) {
getEngine().warn("Compilation of java sources failed");
getEngine().warn("(java compiler (javac) has stopped with errors)");
return false;
}
} catch (IOException ex) {
getEngine().warn("Compilation of java sources failed");
getEngine().warn("(java compiler (javac) invocation failed)");
return false;
}
try {
Class<?> klass = Class.forName(fullClassName, true, new ClassLoader());
return bindDynamicObject(id, klass);
} catch (ClassNotFoundException ex) {
getEngine().warn("Compilation of java sources failed");
getEngine().warn("(Java Class compiled, but not created: " + fullClassName + " )");
return false;
}
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
}
/**
* Calls a method of a Java object.
*/
@Predicate("java_call/3")
public boolean javaCall(Term objId, Term method_name, Term idResult) {
objId = objId.getTerm();
idResult = idResult.getTerm();
Struct method = (Struct) method_name.getTerm();
Object obj = null;
Signature args = null;
String methodName = null;
try {
methodName = method.getName();
// check for accessing field Obj.Field <- set/get(X)
// in that case: objId is '.'(Obj, Field)
if (!objId.isAtom()) {
if (objId instanceof Var) {
return false;
}
Struct sel = (Struct) objId;
if (sel.getName().equals(".") && sel.getArity() == 2 && method.getArity() == 1) {
if (methodName.equals("set"))
return javaSet(sel.getTerm(0), sel.getTerm(1), method.getTerm(0));
else if (methodName.equals("get"))
return javaGet(sel.getTerm(0), sel.getTerm(1), method.getTerm(0));
}
}
args = parseArg(method);
// object and argument must be instantiated
if (objId instanceof Var || args == null)
return false;
//System.out.println(args);
String objName = objId.toStringWithoutApices();
obj = currentObjects.get(objName);
Object res = null;
if (obj != null) {
Class<?> cl = obj.getClass();
//
//
Object[] args_values = args.getValues();
Method m = lookupMethod(cl, methodName, args.getTypes(), args_values);
//
//
if (m != null) {
try {
// works only with JDK 1.2, NOT in Sun Application Server!
//m.setAccessible(true);
res = m.invoke(obj, args_values);
} catch (IllegalAccessException ex) {
getEngine().warn("Method invocation failed: " + methodName+ "( signature: " + args + " )");
ex.printStackTrace();
return false;
}
} else {
getEngine().warn("Method not found: " + methodName+ "( signature: " + args + " )");
return false;
}
} else {
if (objId.isCompound()) {
Struct id = (Struct) objId;
if (id.getArity() == 1 && id.getName().equals("class")) {
try {
Class<?> cl = Class.forName(id.getArg(0).toStringWithoutApices());
Method m = cl.getMethod(methodName, args.getTypes());
m.setAccessible(true);
res = m.invoke(null, args.getValues());
} catch (ClassNotFoundException ex) {
// if not found even as a class id -> consider as a String object value
getEngine().warn("Unknown class.");
ex.printStackTrace();
return false;
}
} else {
// the object is the string itself
Method m = java.lang.String.class.getMethod(methodName, args.getTypes());
m.setAccessible(true);
res = m.invoke(objName, args.getValues());
}
} else {
// the object is the string itself
Method m = java.lang.String.class.getMethod(methodName, args.getTypes());
m.setAccessible(true);
res = m.invoke(objName, args.getValues());
}
}
return parseResult(idResult, res);
} catch (InvocationTargetException ex) {
getEngine().warn("Method failed: " + methodName + " - ( signature: " + args +
" ) - Original Exception: "+ex.getTargetException());
ex.printStackTrace();
return false;
} catch (NoSuchMethodException ex) {
ex.printStackTrace();
getEngine().warn("Method not found: " + methodName+ " - ( signature: " + args + " )");
return false;
} catch (IllegalArgumentException ex) {
ex.printStackTrace();
getEngine().warn("Invalid arguments " + args+ " - ( method: " + methodName + " )");
//ex.printStackTrace();
return false;
} catch (Exception ex) {
ex.printStackTrace();
getEngine().warn("Generic error in method invocation " + methodName);
return false;
}
}
/*
* set the field value of an object
*/
private boolean javaSet(Term objId, Term fieldTerm, Term what) {
//System.out.println("SET "+objId+" "+fieldTerm+" "+what);
what = what.getTerm();
if (!fieldTerm.isAtom() || what instanceof Var)
return false;
String fieldName = ((Struct) fieldTerm).getName();
Object obj = null;
try {
Class<?> cl = null;
if (objId.isCompound() &&
((Struct) objId).getArity() == 1 && ((Struct) objId).getName().equals("class")) {
String clName = ((Struct) objId).getArg(0).toStringWithoutApices();
try {
cl = Class.forName(clName);
} catch (ClassNotFoundException ex) {
getEngine().warn("Java class not found: " + clName);
return false;
} catch (Exception ex) {
getEngine().warn("Static field " + fieldName + " not found in class " + ((Struct) objId).getArg(0).toStringWithoutApices());
return false;
}
} else {
String objName = objId.toStringWithoutApices();
obj = currentObjects.get(objName);
if (obj != null) {
cl = obj.getClass();
} else {
return false;
}
}
// first check for primitive data field
Field field = cl.getField(fieldName);
if (what instanceof Number) {
Number wn = (Number) what;
if (wn instanceof Int) {
field.setInt(obj, wn.intValue());
} else if (wn instanceof alice.tuprolog.Double) {
field.setDouble(obj, wn.doubleValue());
} else if (wn instanceof alice.tuprolog.Long) {
field.setLong(obj, wn.longValue());
} else if (wn instanceof alice.tuprolog.Float) {
field.setFloat(obj, wn.floatValue());
} else {
return false;
}
} else {
String what_name = what.toStringWithoutApices();
Object obj2 = currentObjects.get(what_name);
if (obj2 != null) {
field.set(obj, obj2);
} else {
// consider value as a simple string
field.set(obj, what_name);
}
}
return true;
} catch (NoSuchFieldException ex) {
getEngine().warn("Field " + fieldName + " not found in class " + objId);
return false;
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
}
/*
* get the value of the field
*/
private boolean javaGet(Term objId, Term fieldTerm, Term what) {
//System.out.println("GET "+objId+" "+fieldTerm+" "+what);
if (!fieldTerm.isAtom()) {
return false;
}
String fieldName = ((Struct) fieldTerm).getName();
Object obj = null;
try {
Class<?> cl = null;
if (objId.isCompound() &&
((Struct) objId).getArity() == 1 && ((Struct) objId).getName().equals("class")) {
String clName = ((Struct) objId).getArg(0).toStringWithoutApices();
try {
cl = Class.forName(clName);
} catch (ClassNotFoundException ex) {
getEngine().warn("Java class not found: " + clName);
return false;
} catch (Exception ex) {
getEngine().warn("Static field " + fieldName + " not found in class " + ((Struct) objId).getArg(0).toStringWithoutApices());
return false;
}
} else {
String objName = objId.toStringWithoutApices();
obj = currentObjects.get(objName);
if (obj == null) {
return false;
}
cl = obj.getClass();
}
Field field = cl.getField(fieldName);
Class<?> fc = field.getType();
// work only with JDK 1.2
field.setAccessible(true);
// first check for primitive types
if (fc.equals(Integer.TYPE) || fc.equals(Byte.TYPE)) {
int value = field.getInt(obj);
return unify(what, new alice.tuprolog.Int(value));
} else if (fc.equals(java.lang.Long.TYPE)) {
long value = field.getLong(obj);
return unify(what, new alice.tuprolog.Long(value));
} else if (fc.equals(java.lang.Float.TYPE)) {
float value = field.getFloat(obj);
return unify(what, new alice.tuprolog.Float(value));
} else if (fc.equals(java.lang.Double.TYPE)) {
double value = field.getDouble(obj);
return unify(what, new alice.tuprolog.Double(value));
} else {
// the field value is an object
Object res = field.get(obj);
return bindDynamicObject(what, res);
}
//} catch (ClassNotFoundException ex){
// getEngine().warn("object of unknown class "+objId);
//ex.printStackTrace();
// return false;
} catch (NoSuchFieldException ex) {
getEngine().warn("Field " + fieldName + " not found in class " + objId);
return false;
} catch (Exception ex) {
getEngine().warn("Generic error in accessing the field");
//ex.printStackTrace();
return false;
}
}
@Predicate("java_array_set_primitive/3")
public boolean javaArraySetPrimitive(Term obj_id, Term i, Term what) {
Struct objId = (Struct) obj_id.getTerm();
Number index = (Number) i.getTerm();
what = what.getTerm();
//System.out.println("SET "+objId+" "+fieldTerm+" "+what);
Object obj = null;
if (!index.isInteger()){
return false;
}
try {
Class<?> cl = null;
String objName = objId.toStringWithoutApices();
obj = currentObjects.get(objName);
if (obj != null) {
cl = obj.getClass();
} else {
return false;
}
if (!cl.isArray()){
return false;
}
String name = cl.toString();
if (name.equals("class [I")){
if (!(what instanceof Number)){
return false;
}
byte v = (byte)((Number)what).intValue();
Array.setInt(obj,index.intValue(),v);
} else if (name.equals("class [D")){
if (!(what instanceof Number)){
return false;
}
double v = ((Number)what).doubleValue();
Array.setDouble(obj,index.intValue(),v);
} else if (name.equals("class [F")){
if (!(what instanceof Number)){
return false;
}
float v = ((Number)what).floatValue();
Array.setFloat(obj,index.intValue(),v);
} else if (name.equals("class [L")){
if (!(what instanceof Number)){
return false;
}
long v = ((Number)what).longValue();
Array.setFloat(obj,index.intValue(),v);
} else if (name.equals("class [C")){
String s = what.toString();
Array.setChar(obj,index.intValue(),s.charAt(0));
} else if (name.equals("class [Z")){
String s = what.toString();
if (s.equals("true")){
Array.setBoolean(obj,index.intValue(),true);
} else if (s.equals("false")){
Array.setBoolean(obj,index.intValue(),false);
} else {
return false;
}
} else if (name.equals("class [B")){
if (!(what instanceof Number)){
return false;
}
int v = ((Number)what).intValue();
Array.setByte(obj,index.intValue(),(byte)v);
} else if (name.equals("class [S")){
if (!(what instanceof Number)){
return false;
}
short v = (short)((Number)what).intValue();
Array.setShort(obj,index.intValue(),v);
} else {
return false;
}
return true;
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
}
@Predicate("java_array_get_primitive/3")
public boolean javaArrayGetPrimitive(Term obj_id, Term i, Term what) {
Struct objId = (Struct) obj_id.getTerm();
Number index = (Number) i.getTerm();
what = what.getTerm();
//System.out.println("SET "+objId+" "+fieldTerm+" "+what);
Object obj = null;
if (!index.isInteger()) {
return false;
}
try {
Class<?> cl = null;
String objName = objId.toStringWithoutApices();
obj = currentObjects.get(objName);
if (obj != null) {
cl = obj.getClass();
} else {
return false;
}
if (!cl.isArray()){
return false;
}
String name = cl.toString();
if (name.equals("class [I")){
Term value = new alice.tuprolog.Int(Array.getInt(obj,index.intValue()));
return unify(what,value);
} else if (name.equals("class [D")){
Term value = new alice.tuprolog.Double(Array.getDouble(obj,index.intValue()));
return unify(what,value);
} else if (name.equals("class [F")){
Term value = new alice.tuprolog.Float(Array.getFloat(obj,index.intValue()));
return unify(what,value);
} else if (name.equals("class [L")){
Term value = new alice.tuprolog.Long(Array.getLong(obj,index.intValue()));
return unify(what,value);
} else if (name.equals("class [C")){
Term value = new alice.tuprolog.Struct(""+Array.getChar(obj,index.intValue()));
return unify(what,value);
} else if (name.equals("class [Z")){
boolean b = Array.getBoolean(obj,index.intValue());
if (b) {
return unify(what,alice.tuprolog.Term.TRUE);
} else {
return unify(what,alice.tuprolog.Term.FALSE);
}
} else if (name.equals("class [B")){
Term value = new alice.tuprolog.Int(Array.getByte(obj,index.intValue()));
return unify(what,value);
} else if (name.equals("class [S")){
Term value = new alice.tuprolog.Int(Array.getInt(obj,index.intValue()));
return unify(what,value);
} else {
return false;
}
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
}
private boolean javaArray(String type, int nargs, Term id) {
try {
Object array = null;
String obtype = type.substring(0, type.length() - 2);
if (obtype.equals("boolean")) {
array = new boolean[nargs];
} else if (obtype.equals("byte")) {
array = new byte[nargs];
} else if (obtype.equals("char")) {
array = new char[nargs];
} else if (obtype.equals("short")) {
array = new short[nargs];
} else if (obtype.equals("int")) {
array = new int[nargs];
} else if (obtype.equals("long")) {
array = new long[nargs];
} else if (obtype.equals("float")) {
array = new float[nargs];
} else if (obtype.equals("double")) {
array = new double[nargs];
} else {
Class<?> cl = Class.forName(obtype);
array = Array.newInstance(cl, nargs);
}
return bindDynamicObject(id, array);
} catch (Exception ex) {
//ex.printStackTrace();
return false;
}
}
/**
* creation of method signature from prolog data
*/
private Signature parseArg(Struct method) {
Object[] values = new Object[method.getArity()];
Class<?>[] types = new Class[method.getArity()];
for (int i = 0; i < method.getArity(); i++) {
if (!parseArg(values, types, i, (Term) method.getTerm(i)))
return null;
}
return new Signature(values, types);
}
private Signature parseArg(Object[] objs) {
Object[] values = new Object[objs.length];
Class<?>[] types = new Class[objs.length];
for (int i = 0; i < objs.length; i++) {
if (!parseArg(values, types, i, (Term) objs[i]))
return null;
}
return new Signature(values, types);
}
private boolean parseArg(Object[] values, Class<?>[] types, int i, Term term) {
try {
if (term == null) {
values[i] = null;
types[i] = null;
} else if (term.isAtom()) {
String name = term.toStringWithoutApices();
if (name.equals("true")){
values[i]=Boolean.TRUE;
types[i] = Boolean.TYPE;
} else if (name.equals("false")){
values[i]=Boolean.FALSE;
types[i] = Boolean.TYPE;
} else {
Object obj = currentObjects.get(name);
if (obj == null) {
values[i] = name;
} else {
values[i] = obj;
}
types[i] = values[i].getClass();
}
} else if (term instanceof Number) {
Number t = (Number) term;
if (t instanceof Int) {
values[i] = new java.lang.Integer(t.intValue());
types[i] = java.lang.Integer.TYPE;
} else if (t instanceof alice.tuprolog.Double) {
values[i] = new java.lang.Double(t.doubleValue());
types[i] = java.lang.Double.TYPE;
} else if (t instanceof alice.tuprolog.Long) {
values[i] = new java.lang.Long(t.longValue());
types[i] = java.lang.Long.TYPE;
} else if (t instanceof alice.tuprolog.Float) {
values[i] = new java.lang.Float(t.floatValue());
types[i] = java.lang.Float.TYPE;
}
} else if (term instanceof Struct) {
// argument descriptors
Struct tc = (Struct) term;
if (tc.getName().equals("as")) {
return parseAs(values, types, i, tc.getTerm(0), tc.getTerm(1));
} else {
Object obj = currentObjects.get(tc.toStringWithoutApices());
if (obj == null) {
values[i] = tc.toStringWithoutApices();
} else {
values[i] = obj;
}
types[i] = values[i].getClass();
}
} else if (term instanceof Var && !((Var) term).isBound()) {
values[i] = null;
types[i] = Object.class;
} else {
return false;
}
} catch (Exception ex) {
return false;
}
return true;
}
/**
* parsing 'as' operator, which makes it possible
* to define the specific class of an argument
*/
private boolean parseAs(Object[] values, Class<?>[] types, int i, Term castWhat, Term castTo) {
try {
if (!(castWhat instanceof Number)) {
String castTo_name = ((Struct) castTo).toStringWithoutApices();
String castWhat_name = castWhat.getTerm().toStringWithoutApices();
//System.out.println(castWhat_name+" "+castTo_name);
if (castTo_name.equals("java.lang.String") &&
castWhat_name.equals("true")){
values[i]="true";
types[i]=String.class;
return true;
} else if (castTo_name.equals("java.lang.String") &&
castWhat_name.equals("false")){
values[i]="false";
types[i]=String.class;
return true;
} else if (castTo_name.endsWith("[]")) {
if (castTo_name.equals("boolean[]")) {
castTo_name = "[Z";
} else if (castTo_name.equals("byte[]")) {
castTo_name = "[B";
} else if (castTo_name.equals("short[]")) {
castTo_name = "[S";
} else if (castTo_name.equals("char[]")) {
castTo_name = "[C";
} else if (castTo_name.equals("int[]")) {
castTo_name = "[I";
} else if (castTo_name.equals("long[]")) {
castTo_name = "[L";
} else if (castTo_name.equals("float[]")) {
castTo_name = "[F";
} else if (castTo_name.equals("double[]")) {
castTo_name = "[D";
} else {
castTo_name = "[L" + castTo_name.substring(0, castTo_name.length() - 2) + ";";
}
}
if (!castWhat_name.equals("null")) {
Object obj_to_cast = currentObjects.get(castWhat_name);
if (obj_to_cast == null) {
if (castTo_name.equals("boolean")) {
if (castWhat_name.equals("true")) {
values[i] = new Boolean(true);
} else if (castWhat_name.equals("false")) {
values[i] = new Boolean(false);
} else {
return false;
}
types[i] = Boolean.TYPE;
} else {
// conversion to array
return false;
}
} else {
values[i] = obj_to_cast;
try {
types[i] = (Class.forName(castTo_name));
} catch (ClassNotFoundException ex) {
getEngine().warn("Java class not found: " + castTo_name);
return false;
}
}
} else {
values[i] = null;
if (castTo_name.equals("byte")) {
types[i] = Byte.TYPE;
} else if (castTo_name.equals("short")) {
types[i] = Short.TYPE;
} else if (castTo_name.equals("char")) {
types[i] = Character.TYPE;
} else if (castTo_name.equals("int")) {
types[i] = java.lang.Integer.TYPE;
} else if (castTo_name.equals("long")) {
types[i] = java.lang.Long.TYPE;
} else if (castTo_name.equals("float")) {
types[i] = java.lang.Float.TYPE;
} else if (castTo_name.equals("double")) {
types[i] = java.lang.Double.TYPE;
} else if (castTo_name.equals("boolean")) {
types[i] = java.lang.Boolean.TYPE;
} else {
try {
types[i] = (Class.forName(castTo_name));
} catch (ClassNotFoundException ex) {
getEngine().warn("Java class not found: " + castTo_name);
return false;
}
}
}
} else {
Number num = (Number) castWhat;
String castTo_name = ((Struct) castTo).getName();
if (castTo_name.equals("byte")) {
values[i] = new Byte((byte) num.intValue());
types[i] = Byte.TYPE;
} else if (castTo_name.equals("short")) {
values[i] = new Short((short) num.intValue());
types[i] = Short.TYPE;
} else if (castTo_name.equals("int")) {
values[i] = new Integer(num.intValue());
types[i] = Integer.TYPE;
} else if (castTo_name.equals("long")) {
values[i] = new java.lang.Long(num.longValue());
types[i] = java.lang.Long.TYPE;
} else if (castTo_name.equals("float")) {
values[i] = new java.lang.Float(num.floatValue());
types[i] = java.lang.Float.TYPE;
} else if (castTo_name.equals("double")) {
values[i] = new java.lang.Double(num.doubleValue());
types[i] = java.lang.Double.TYPE;
} else {
return false;
}
}
} catch (Exception ex) {
getEngine().warn("Casting " + castWhat + " to " + castTo + " failed");
return false;
}
return true;
}
/** parses return value of a method invocation */
private boolean parseResult(Term id, Object obj) {
if (obj == null) {
//return unify(id,Term.TRUE);
return unify(id, new Var());
}
try {
if (Boolean.class.isInstance(obj)) {
if (((Boolean) obj).booleanValue()) {
return unify(id, Term.TRUE);
} else {
return unify(id, Term.FALSE);
}
} else if (Byte.class.isInstance(obj)) {
return unify(id, new Int(((Byte) obj).intValue()));
} else if (Short.class.isInstance(obj)) {
return unify(id, new Int(((Short) obj).intValue()));
} else if (Integer.class.isInstance(obj)) {
return unify(id, new Int(((Integer) obj).intValue()));
} else if (java.lang.Long.class.isInstance(obj)) {
return unify(id, new alice.tuprolog.Long(((java.lang.Long) obj).longValue()));
} else if (java.lang.Float.class.isInstance(obj)) {
return unify(id, new alice.tuprolog.Float(((java.lang.Float) obj).floatValue()));
} else if (java.lang.Double.class.isInstance(obj)) {
return unify(id, new alice.tuprolog.Double(((java.lang.Double) obj).doubleValue()));
} else if (String.class.isInstance(obj)) {
return unify(id, new Struct((String) obj));
} else if (Character.class.isInstance(obj)) {
return unify(id, new Struct(((Character) obj).toString()));
} else {
return bindDynamicObject(id, obj);
}
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
}
private Object[] getArrayFromList(Struct list) {
Object args[] = new Object[list.listSize()];
int count = 0;
for (Term t : list)
args[count++] = t;
return args;
}
/**
* Register an object with the specified id.
* The life-time of the link to the object is engine life-time,
* available besides the individual query.
*
* The identifier must be a ground object.
*
* @param id object identifier
* @param obj the object
* @return true if the operation is successful
* @throws InvalidObjectIdException if the object id is not valid
*/
public boolean register(Struct id, Object obj) throws InvalidObjectIdException {
/*
* note that this method act on the staticObject
* and staticObject_inverse hashmaps
*/
if (!id.isGround()) {
throw new InvalidObjectIdException();
}
// already registered object?
synchronized (staticObjects){
Object aKey = staticObjectsInverse.get(obj);
if (aKey != null) {
// object already referenced
return false;
} else {
String raw_name = id.getTerm().toStringWithoutApices();
staticObjects.put(raw_name, obj);
staticObjectsInverse.put(obj, id);
return true;
}
}
}
/**
* Registers an object, with automatic creation of the identifier.
*
* If the object is already registered,
* its identifier is returned
*
* @param obj object to be registered.
* @return fresh id
*/
public Struct register(Object obj) {
//System.out.println("lib: "+this+" current id: "+this.id);
// already registered object?
synchronized (staticObjects){
Object aKey = staticObjectsInverse.get(obj);
if (aKey != null) {
// object already referenced -> unifying terms
// referencing the object
//log("obj already registered: unify "+id+" "+aKey);
return (Struct) aKey;
} else {
Struct id = generateFreshId();
staticObjects.put(id.getName(), obj);
staticObjectsInverse.put(obj, id);
return id;
}
}
}
/**
* Gets the reference to an object previously registered
*
* @param id object id
* @return the object, if present
* @throws InvalidObjectIdException
*/
public Object getRegisteredObject(Struct id) throws InvalidObjectIdException {
if (!id.isGround()) {
throw new InvalidObjectIdException();
}
synchronized (staticObjects){
return staticObjects.get(id.toStringWithoutApices());
}
}
/**
* Unregisters an object, given its identifier
*
*
* @param id object identifier
* @return true if the operation is successful
* @throws InvalidObjectIdException if the id is not valid (e.g. is not ground)
*/
public boolean unregister(Struct id) throws InvalidObjectIdException {
if (!id.isGround()) {
throw new InvalidObjectIdException();
}
synchronized (staticObjects){
String raw_name = id.toStringWithoutApices();
Object obj = staticObjects.remove(raw_name);
if (obj != null) {
staticObjectsInverse.remove(obj);
return true;
} else {
return false;
}
}
}
/**
* Registers an object only for the running query life-time
*
* @param id object identifier
* @param obj object
*/
public void registerDynamic(Struct id, Object obj) {
synchronized (currentObjects){
String raw_name = id.toStringWithoutApices();
currentObjects.put(raw_name, obj);
currentObjectsInverse.put(obj, id);
}
}
/**
* Registers an object for the query life-time,
* with the automatic generation of the identifier.
*
* If the object is already registered,
* its identifier is returned
*
* @param obj object to be registered
* @return identifier
*/
public Struct registerDynamic(Object obj) {
//System.out.println("lib: "+this+" current id: "+this.id);
// already registered object?
synchronized (currentObjects){
Object aKey = currentObjectsInverse.get(obj);
if (aKey != null) {
// object already referenced -> unifying terms
// referencing the object
//log("obj already registered: unify "+id+" "+aKey);
return (Struct) aKey;
} else {
Struct id = generateFreshId();
currentObjects.put(id.getName(), obj);
currentObjectsInverse.put(obj, id);
return id;
}
}
}
/**
* Gets a registered dynamic object
* (returns null if not presents)
*/
public Object getRegisteredDynamicObject(Struct id) throws InvalidObjectIdException {
if (!id.isGround()) {
throw new InvalidObjectIdException();
}
synchronized (currentObjects){
return currentObjects.get(id.toStringWithoutApices());
}
}
/**
* Unregister the object, only for dynamic case
*
* @param id object identifier
* @return true if the operation is successful
*/
public boolean unregisterDynamic(Struct id) {
synchronized (currentObjects){
String raw_name = id.toStringWithoutApices();
Object obj = currentObjects.remove(raw_name);
if (obj != null) {
currentObjectsInverse.remove(obj);
return true;
} else {
return false;
}
}
}
/**
* Tries to bind specified id to a provided java object.
*
* Term id can be a variable or a ground term.
*/
protected boolean bindDynamicObject(Term id, Object obj) {
// null object are considered to _ variable
if (obj == null) {
return unify(id, new Var());
}
// already registered object?
synchronized (currentObjects){
Object aKey = currentObjectsInverse.get(obj);
if (aKey != null) {
// object already referenced -> unifying terms
// referencing the object
//log("obj already registered: unify "+id+" "+aKey);
return unify(id, (Term) aKey);
} else {
// object not previously referenced
if (id instanceof Var) {
// get a ground term
Struct idTerm = generateFreshId();
unify(id, idTerm);
registerDynamic(idTerm, obj);
//log("not ground id for a new obj: "+id+" as ref for "+obj);
return true;
} else {
// verify of the id is already used
String raw_name = id.getTerm().toStringWithoutApices();
Object linkedobj = currentObjects.get(raw_name);
if (linkedobj == null) {
registerDynamic((Struct)(id.getTerm()), obj);
//log("ground id for a new obj: "+id+" as ref for "+obj);
return true;
} else {
// an object with the same id is already
// present: must be the same object
return obj == linkedobj;
}
}
}
}
}
/**
* Generates a fresh numeric identifier
* @return
*/
protected Struct generateFreshId() {
return new Struct("$obj_" + id++);
}
// --------------------------------------------------
private static Method lookupMethod(Class<?> target, String name,
Class<?>[] argClasses, Object[] argValues) throws NoSuchMethodException {
// first try for exact match
try {
Method m = target.getMethod(name, argClasses);
return m;
} catch (NoSuchMethodException e) {
if (argClasses.length == 0) // if no arguments & no exact match, out of luck
return null;
}
// go the more complicated route
Method[] methods = target.getMethods();
List<Method> goodMethods = new ArrayList<Method>();
for (int i = 0; i != methods.length; i++) {
if (name.equals(methods[i].getName()) &&
matchClasses(methods[i].getParameterTypes(), argClasses))
goodMethods.add(methods[i]);
}
switch (goodMethods.size()) {
case 0:
// no methods have been found checking for assignability
// and (int -> long) conversion. One last chance:
// looking for compatible methods considering also
// type conversions:
// double --> float
// (the first found is used - no most specific
// method algorithm is applied )
for (int i = 0; i != methods.length; i++) {
if (name.equals(methods[i].getName())) {
Class<?>[] types = methods[i].getParameterTypes();
Object[] val = matchClasses(types, argClasses, argValues);
if (val != null) {
// found a method compatible
// after type conversions
for (int j = 0; j < types.length; j++) {
argClasses[j] = types[j];
argValues[j] = val[j];
}
return methods[i];
}
}
}
return null;
case 1:
return goodMethods.get(0);
default:
return mostSpecificMethod(goodMethods);
}
}
private static Constructor<?> lookupConstructor(Class<?> target, Class<?>[] argClasses, Object[] argValues) throws NoSuchMethodException {
// first try for exact match
try {
return target.getConstructor(argClasses);
} catch (NoSuchMethodException e) {
if (argClasses.length == 0) { // if no args & no exact match, out of luck
return null;
}
}
// go the more complicated route
Constructor<?>[] constructors = target.getConstructors();
List<Constructor<?>> goodConstructors = new ArrayList<Constructor<?>>();
for (int i = 0; i != constructors.length; i++) {
if (matchClasses(constructors[i].getParameterTypes(), argClasses))
goodConstructors.add(constructors[i]);
}
switch (goodConstructors.size()) {
case 0:
// no constructors have been found checking for assignability
// and (int -> long) conversion. One last chance:
// looking for compatible methods considering also
// type conversions:
// double --> float
// (the first found is used - no most specific
// method algorithm is applied )
for (int i = 0; i != constructors.length; i++) {
Class<?>[] types = constructors[i].getParameterTypes();
Object[] val = matchClasses(types, argClasses, argValues);
if (val != null) {
// found a compatible method after type conversions
for (int j = 0; j < types.length; j++) {
argClasses[j] = types[j];
argValues[j] = val[j];
}
return constructors[i];
}
}
return null;
case 1:
return goodConstructors.get(0);
default:
return mostSpecificConstructor(goodConstructors);
}
}
// 1st argument is from method, 2nd is actual parameters
private static boolean matchClasses(Class<?>[] mclasses, Class<?>[] pclasses) {
if (mclasses.length == pclasses.length) {
for (int i = 0; i != mclasses.length; i++) {
if (!matchClass(mclasses[i], pclasses[i])) {
return false;
}
}
return true;
}
return false;
}
private static boolean matchClass(Class<?> mclass, Class<?> pclass) {
boolean assignable = mclass.isAssignableFrom(pclass);
if (assignable) {
return true;
} else {
if (mclass.equals(java.lang.Long.TYPE) && (pclass.equals(java.lang.Integer.TYPE))) {
return true;
}
}
return false;
}
private static Method mostSpecificMethod(List<Method> methods) throws NoSuchMethodException {
for (int i = 0; i != methods.size(); i++) {
for (int j = 0; j != methods.size(); j++) {
if ((i != j) &&
(moreSpecific(methods.get(i), methods.get(j)))) {
methods.remove(j);
if (i > j) i--;
j--;
}
}
}
if (methods.size() == 1)
return methods.get(0);
else
throw new NoSuchMethodException(">1 most specific method");
}
// true if c1 is more specific than c2
private static boolean moreSpecific(Method c1, Method c2) {
Class<?>[] p1 = c1.getParameterTypes();
Class<?>[] p2 = c2.getParameterTypes();
int n = p1.length;
for (int i = 0; i != n; i++) {
if (!matchClass(p2[i], p1[i])) {
return false;
}
}
return true;
}
private static Constructor<?> mostSpecificConstructor(List<Constructor<?>> constructors) throws NoSuchMethodException {
for (int i = 0; i != constructors.size(); i++) {
for (int j = 0; j != constructors.size(); j++) {
if ((i != j) &&
(moreSpecific(constructors.get(i), constructors.get(j)))) {
constructors.remove(j);
if (i > j) i--;
j--;
}
}
}
if (constructors.size() == 1)
return constructors.get(0);
else
throw new NoSuchMethodException(">1 most specific constructor");
}
// true if c1 is more specific than c2
private static boolean moreSpecific(Constructor<?> c1, Constructor<?> c2) {
Class<?>[] p1 = c1.getParameterTypes();
Class<?>[] p2 = c2.getParameterTypes();
int n = p1.length;
for (int i = 0; i != n; i++) {
if (!matchClass(p2[i], p1[i])) {
return false;
}
}
return true;
}
// Checks compatibility also considering explicit type conversion.
// The method returns the argument values, since they could be changed
// after a type conversion.
//
// In particular the check must be done for the DEFAULT type of tuProlog,
// that are int and double; so
// (required X, provided a DEFAULT -
// with DEFAULT to X conversion 'conceivable':
// for instance *double* to *int* is NOT considered good
//
// required a float, provided an int OK
// required a double, provided a int OK
// required a long, provided a int ==> already considered by
// previous match test
// required a float, provided a double OK
// required a int, provided a double => NOT CONSIDERED
// required a long, provided a double => NOT CONSIDERED
//
private static Object[] matchClasses(Class<?>[] mclasses, Class<?>[] pclasses, Object[] values) {
if (mclasses.length == pclasses.length) {
Object[] newvalues = new Object[mclasses.length];
for (int i = 0; i != mclasses.length; i++) {
boolean assignable = mclasses[i].isAssignableFrom(pclasses[i]);
if (assignable ||
(mclasses[i].equals(java.lang.Long.TYPE) && pclasses[i].equals(java.lang.Integer.TYPE))) {
newvalues[i] = values[i];
} else if (mclasses[i].equals(java.lang.Float.TYPE) &&
pclasses[i].equals(java.lang.Double.TYPE)) {
// arg required: a float, arg provided: a double
// so we need an explicit conversion...
newvalues[i] = new java.lang.Float(((java.lang.Double) values[i]).floatValue());
} else if (mclasses[i].equals(java.lang.Float.TYPE) &&
pclasses[i].equals(java.lang.Integer.TYPE)) {
// arg required: a float, arg provided: an int
// so we need an explicit conversion...
newvalues[i] = new java.lang.Float(((java.lang.Integer) values[i]).intValue());
} else if (mclasses[i].equals(java.lang.Double.TYPE) &&
pclasses[i].equals(java.lang.Integer.TYPE)) {
// arg required: a double, arg provided: an int
// so we need an explicit conversion...
newvalues[i] = new java.lang.Double(((java.lang.Integer) values[i]).doubleValue());
} else if (values[i] == null && !mclasses[i].isPrimitive()) {
newvalues[i] = null;
} else {
return null;
}
}
return newvalues;
} else {
return null;
}
}
}
/**
* Signature class maintains information about
* type and value of a method arguments
*/
class Signature {
Class<?>[] types;
Object[] values;
public Signature(Object[] v, Class<?>[] c) {
values = v;
types = c;
}
public Class<?>[] getTypes() {
return types;
}
Object[] getValues() {
return values;
}
@Override
public String toString() {
String st = "";
for (int i = 0; i < types.length; i++) {
st = st + "\n Argument " + i + " - VALUE: " + values[i] + " TYPE: " + types[i];
}
return st;
}
}
/** used to load new classes without touching system class loader */
class ClassLoader extends java.lang.ClassLoader {
}