alice.tuprolog.lib.OOLibrary.java Source code

Java tutorial

Introduction

Here is the source code for alice.tuprolog.lib.OOLibrary.java

Source

/*
 * 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.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
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.net.MalformedURLException;
import java.net.URL;
import java.util.EventListener;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;

import alice.tuprolog.*;
import alice.tuprolog.Number;
import alice.tuprolog.PTerm;
import alice.util.AbstractDynamicClassLoader;
import alice.util.InspectionUtils;
import alice.util.JavaDynamicClassLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * This class represents a tuProlog library enabling the interaction with the
 * Java environment from tuProlog.
 * 
 * Warning we use the 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
 */
@SuppressWarnings("serial")
public class OOLibrary extends Library {

    /**
     * java objects referenced by prolog terms (keys)
     */
    private HashMap<String, Object> currentObjects = new HashMap<>();
    /**
     * inverse map useful for implementation issue
     */
    private IdentityHashMap<Object, Struct> currentObjects_inverse = new IdentityHashMap<>();

    private final HashMap<String, Object> staticObjects = new HashMap<>();
    private final IdentityHashMap<Object, Struct> staticObjects_inverse = new IdentityHashMap<>();

    /**
     * progressive counter used to identify registered objects
     */
    private int id;
    /**
     * progressive counter used to generate lambda function dinamically
     */
    private int counter;

    /**
    * @author Alessio Mercurio
    * 
    * used to manage different classloaders.
    */
    private final AbstractDynamicClassLoader dynamicLoader;

    /**
     * library theory
     */

    public OOLibrary() {
        //       if (System.getProperty("java.vm.name").equals("Dalvik"))
        //      {
        //         dynamicLoader = new AndroidDynamicClassLoader(new URL[] {}, getClass().getClassLoader());
        //      }
        //      else
        //      {
        dynamicLoader = new JavaDynamicClassLoader(new URL[] {}, getClass().getClassLoader());
        //      }
    }

    @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" + "new_object_bt(ClassName,Args,Id):- new_object(ClassName,Args,Id).\n"
                + "new_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"

                + "array_set(Array,Index,Object):- class('java.lang.reflect.Array') <- set(Array as 'java.lang.Object',Index,Object as 'java.lang.Object'), !.\n"
                + "array_set(Array,Index,Object):- java_array_set_primitive(Array,Index,Object).\n"
                + "array_get(Array,Index,Object):- class('java.lang.reflect.Array') <- get(Array as 'java.lang.Object',Index) returns Object,!.\n"
                + "array_get(Array,Index,Object):- java_array_get_primitive(Array,Index,Object).\n"

                + "array_length(Array,Length):- class('java.lang.reflect.Array') <- getLength(Array as 'java.lang.Object') returns Length.\n"

                + //**** following section deprecated from tuProlog 3.0  ***//
                "java_object_bt(ClassName,Args,Id):- java_object(ClassName,Args,Id).\n"
                + "java_object_bt(ClassName,Args,Id):- destroy_object(Id).\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" + //**** end section deprecated from tuProlog 3.0  ***//
                "java_catch(JavaGoal, List, Finally) :- call(JavaGoal), call(Finally).\n";

    }

    @Override
    public void dismiss() {
        currentObjects.clear();
        currentObjects_inverse.clear();
    }

    public void dismissAll() {
        currentObjects.clear();
        currentObjects_inverse.clear();
        staticObjects.clear();
        staticObjects_inverse.clear();
    }

    @Override
    public void onSolveBegin(PTerm goal) {
        currentObjects.clear();
        currentObjects_inverse.clear();
        Iterator<Map.Entry<Object, Struct>> it = staticObjects_inverse.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<Object, Struct> en = it.next();
            bindDynamicObject(en.getValue(), en.getKey());
        }
        preregisterObjects();
    }

    @Override
    public void onSolveEnd() {
    }

    /**
     * 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();
        }
    }

    /**
    * Deprecated from tuProlog 3.0 use new_object
    */
    public boolean java_object_3(PTerm className, PTerm argl, PTerm id) throws JavaException {
        return new_object_3(className, argl, id);
    }

    /**
     * Creates of a java object - not backtrackable case
     * @param className
     * @param argl
     * @param id
     * @return
     * @throws JavaException
     */
    public boolean new_object_3(PTerm className, PTerm argl, PTerm id) throws JavaException {
        className = className.getTerm();
        Struct arg = (Struct) argl.getTerm();
        id = id.getTerm();
        try {
            if (!className.isAtom()) {
                throw new JavaException(new ClassNotFoundException("Java class not found: " + className));
            }
            String clName = ((Struct) className).getName();
            // check for array type
            if (clName.endsWith("[]")) {
                Object[] list = getArrayFromList(arg);
                int nargs = ((Number) list[0]).intValue();
                if (java_array(clName, nargs, id))
                    return true;
                else
                    throw new JavaException(new Exception());
            }
            Signature args = parseArg(getArrayFromList(arg));
            if (args == null) {
                throw new IllegalArgumentException("Illegal constructor arguments  " + arg);
            }
            // object creation with argument described in args
            try {
                Class<?> cl = Class.forName(clName, true, dynamicLoader);
                Object[] args_value = args.getValues();
                Constructor<?> co = lookupConstructor(cl, args.getTypes(), args_value);
                if (co == null) {
                    getEngine().logger.warn("Constructor not found: class " + clName);
                    throw new JavaException(new NoSuchMethodException("Constructor not found: class " + clName));
                }

                Object obj = co.newInstance(args_value);
                if (bindDynamicObject(id, obj))
                    return true;
                else
                    throw new JavaException(new Exception());
            } catch (ClassNotFoundException ex) {
                getEngine().logger.warn("Java class not found: " + clName);
                throw new JavaException(ex);
            } catch (InvocationTargetException ex) {
                getEngine().logger.warn("Invalid constructor arguments.");
                throw new JavaException(ex);
            } catch (NoSuchMethodException ex) {
                getEngine().logger.warn("Constructor not found: " + args.getTypes());
                throw new JavaException(ex);
            } catch (InstantiationException ex) {
                getEngine().logger.warn("Objects of class " + clName + " cannot be instantiated");
                throw new JavaException(ex);
            } catch (IllegalArgumentException ex) {
                getEngine().logger.warn("Illegal constructor arguments  " + args);
                throw new JavaException(ex);
            }
        } catch (Exception ex) {
            throw new JavaException(ex);
        }
    }

    /**
     * @author Roberta Calegari
     * 
     * Creates of a lambda object - not backtrackable case
     * @param interfaceName represent the name of the target interface i.e. 'java.util.function.Predicate<String>'
     * @param implementation contains the function implementation i.e. 's -> s.length()>4 '
     * @param id represent the identification_name of the created object function i.e. MyLambda
     * 
     * @throws JavaException, Exception
     */
    @SuppressWarnings("unchecked")
    public <T> boolean new_lambda_3(PTerm interfaceName, PTerm implementation, PTerm id)
            throws JavaException, Exception {
        try {
            counter++;
            String target_class = (interfaceName.toString()).substring(1, interfaceName.toString().length() - 1);
            String lambda_expression = (implementation.toString()).substring(1,
                    implementation.toString().length() - 1);
            //following lines allow to delete escape char from received string
            target_class = org.apache.commons.lang3.StringEscapeUtils.unescapeJava(target_class);
            lambda_expression = org.apache.commons.lang3.StringEscapeUtils.unescapeJava(lambda_expression);

            Class<?> lambdaMetaFactory = alice.util.proxyGenerator.Generator.make(
                    java.lang.ClassLoader.getSystemClassLoader(), "MyLambdaFactory" + counter,
                    "public class MyLambdaFactory" + counter + " {\n" + "  public " + target_class
                            + " getFunction() {\n" + "       return " + lambda_expression + "; \n" + "  }\n"
                            + "}\n");

            Object myLambdaFactory = lambdaMetaFactory.newInstance();
            Class<?> myLambdaClass = myLambdaFactory.getClass();
            Method[] allMethods = myLambdaClass.getDeclaredMethods();
            T myLambdaInstance = null;
            for (Method m : allMethods) {
                String mname = m.getName();
                if (mname.startsWith("getFunction"))
                    myLambdaInstance = (T) m.invoke(myLambdaFactory);
            }
            id = id.getTerm();
            if (bindDynamicObject(id, myLambdaInstance))
                return true;
            else
                throw new JavaException(new Exception());
        } catch (Exception ex) {
            throw new JavaException(ex);
        }
    }

    /**
     * Destroy the link to a java object - called not directly, but from
     * predicate java_object (as second choice, for backtracking)
     * 
     * @throws JavaException
     */
    public boolean destroy_object_1(PTerm id) throws JavaException {
        id = id.getTerm();
        try {
            if (id.isGround()) {
                unregisterDynamic((Struct) id);
            }
            return true;
        } catch (Exception ex) {
            throw new JavaException(ex);
        }
    }

    /**
     * Deprecated from tuProlog 3.0 use new_class
     * 
     * @throws JavaException
     */
    public boolean java_class_4(PTerm clSource, PTerm clName, PTerm clPathes, PTerm id) throws JavaException {
        return new_class_4(clSource, clName, clPathes, id);
    }

    /**
     * The java class/4 creates, compiles and loads a new Java class from a source text
     * @param clSource: is a string representing the text source of the new Java class
     * @param clName: full class name
     * @param clPathes: is a (possibly empty) Prolog list of class paths that may be required for a successful dynamic compilation of this class
     * @param id: reference to an instance of the meta-class java.lang.Class rep- resenting the newly-created class
     * @return boolean: true if created false otherwise
     * @throws JavaException
     */
    public boolean new_class_4(PTerm clSource, PTerm clName, PTerm clPathes, PTerm id) throws JavaException {
        Struct classSource = (Struct) clSource.getTerm();
        Struct className = (Struct) clName.getTerm();
        Struct classPathes = (Struct) clPathes.getTerm();
        id = id.getTerm();
        try {
            String fullClassName = alice.util.Tools.removeApices(className.toString());

            String fullClassPath = fullClassName.replace('.', '/');
            Iterator<? extends PTerm> it = classPathes.listIterator();
            String cp = "";
            while (it.hasNext()) {
                if (cp.length() > 0) {
                    cp += ";";
                }
                cp += alice.util.Tools.removeApices(it.next().toString());
            }
            if (cp.length() > 0) {
                cp = " -classpath " + cp;
            }

            String text = alice.util.Tools.removeApices(classSource.toString());
            try {
                FileWriter file = new FileWriter(fullClassPath + ".java");
                file.write(text);
                file.close();
            } catch (IOException ex) {
                getEngine().logger.warn("Compilation of java sources failed");
                getEngine().logger.warn("(creation of " + fullClassPath + ".java fail failed)");
                throw new JavaException(ex);
            }
            String cmd = "javac " + cp + ' ' + fullClassPath + ".java";

            try {
                Process jc = Runtime.getRuntime().exec(cmd);
                int res = jc.waitFor();
                if (res != 0) {
                    getEngine().logger.warn("Compilation of java sources failed");
                    getEngine().logger.warn("(java compiler (javac) has stopped with errors)");
                    throw new IOException("Compilation of java sources failed");
                }
            } catch (IOException ex) {
                getEngine().logger.warn("Compilation of java sources failed");
                getEngine().logger.warn("(java compiler (javac) invocation failed)");
                throw new JavaException(ex);
            }
            try {
                Class<?> the_class;

                /**
                 * @author Alessio Mercurio
                 * 
                 * On Dalvik VM we can only use the DexClassLoader.
                 */

                the_class = System.getProperty("java.vm.name").equals("Dalvik")
                        ? Class.forName(fullClassName, true, dynamicLoader)
                        : Class.forName(fullClassName, true, new ClassLoader());

                if (bindDynamicObject(id, the_class))
                    return true;
                else
                    throw new JavaException(new Exception());
            } catch (ClassNotFoundException ex) {
                getEngine().logger.warn("Compilation of java sources failed");
                getEngine().logger.warn("(Java Class compiled, but not created: " + fullClassName + " )");
                throw new JavaException(ex);
            }
        } catch (Exception ex) {
            throw new JavaException(ex);
        }
    }

    /**
    * 
    * Calls a method of a Java object
    * 
    * @throws JavaException
    * 
    */
    public boolean java_call_3(PTerm objId, PTerm method_name, PTerm idResult) throws JavaException {
        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();
            if (!objId.isAtom()) {
                if (objId instanceof Var) {
                    throw new JavaException(new IllegalArgumentException(objId.toString()));
                }
                Struct sel = (Struct) objId;
                if (sel.getName().equals(".") && sel.getArity() == 2 && method.getArity() == 1) {
                    if (methodName.equals("set")) {
                        return java_set(sel.getTerm(0), sel.getTerm(1), method.getTerm(0));
                    } else if (methodName.equals("get")) {
                        return java_get(sel.getTerm(0), sel.getTerm(1), method.getTerm(0));
                    }
                }
            }
            args = parseArg(method);
            // object and argument must be instantiated
            if (objId instanceof Var)
                throw new JavaException(new IllegalArgumentException(objId.toString()));
            if (args == null) {
                throw new JavaException(new IllegalArgumentException());
            }
            String objName = alice.util.Tools.removeApices(objId.toString());
            obj = staticObjects.containsKey(objName) ? staticObjects.get(objName) : 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 {
                        m.setAccessible(true);
                        res = m.invoke(obj, args_values);
                    } catch (IllegalAccessException ex) {
                        getEngine().logger
                                .warn("Method invocation failed: " + methodName + "( signature: " + args + " )");
                        throw new JavaException(ex);
                    }
                } else {
                    getEngine().logger.warn("Method not found: " + methodName + "( signature: " + args + " )");
                    throw new JavaException(new NoSuchMethodException(
                            "Method not found: " + methodName + "( signature: " + args + " )"));
                }
            } else {
                if (objId.isCompound()) {
                    Struct id = (Struct) objId;

                    if (id.getArity() == 1 && id.getName().equals("class")) {
                        try {
                            String clName = alice.util.Tools.removeApices(id.getArg(0).toString());
                            Class<?> cl = Class.forName(clName, true, dynamicLoader);

                            Method m = InspectionUtils.searchForMethod(cl, 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().logger.warn("Unknown class.");
                            throw new JavaException(ex);
                        }
                    } 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());
                }
            }
            if (parseResult(idResult, res))
                return true;
            else
                throw new JavaException(new Exception());
        } catch (InvocationTargetException ex) {
            getEngine().logger.warn("Method failed: " + methodName + " - ( signature: " + args
                    + " ) - Original Exception: " + ex.getTargetException());
            throw new JavaException(new IllegalArgumentException());
        } catch (NoSuchMethodException ex) {
            getEngine().logger.warn("Method not found: " + methodName + " - ( signature: " + args + " )");
            throw new JavaException(ex);
        } catch (IllegalArgumentException ex) {
            getEngine().logger.warn("Invalid arguments " + args + " - ( method: " + methodName + " )");
            throw new JavaException(ex);
        } catch (Exception ex) {
            getEngine().logger.warn("Generic error in method invocation " + methodName);
            throw new JavaException(ex);
        }
    }

    /**
     * @author Michele Mannino
     * 
     * Set global classpath
     * 
     * @throws JavaException
     * 
     */
    public boolean set_classpath_1(PTerm paths) throws JavaException {
        try {
            paths = paths.getTerm();
            if (!paths.isList())
                throw new IllegalArgumentException();
            String[] listOfPaths = getStringArrayFromStruct((Struct) paths);
            dynamicLoader.removeAllURLs();
            dynamicLoader.addURLs(getURLsFromStringArray(listOfPaths));
            return true;
        } catch (IllegalArgumentException e) {
            getEngine().logger.warn("Illegal list of paths " + paths);
            throw new JavaException(e);
        } catch (Exception e) {
            throw new JavaException(e);
        }
    }

    /**
     * @author Michele Mannino
     * 
     * Get global classpath
     * 
     * @throws JavaException
     * 
     */

    public boolean get_classpath_1(PTerm paths) throws JavaException {
        try {
            paths = paths.getTerm();
            if (!(paths instanceof Var))
                throw new IllegalArgumentException();
            URL[] urls = dynamicLoader.getURLs();
            String stringURLs = null;
            PTerm pathTerm = null;
            if (urls.length > 0) {
                stringURLs = "[";

                for (URL url : urls) {
                    File file = new File(java.net.URLDecoder.decode(url.getFile(), "UTF-8"));
                    stringURLs = stringURLs + '\'' + file.getPath() + "',";
                }

                stringURLs = stringURLs.substring(0, stringURLs.length() - 1);
                stringURLs = stringURLs + ']';
            } else
                stringURLs = "[]";
            pathTerm = PTerm.createTerm(stringURLs);
            return unify(paths, pathTerm);
        } catch (IllegalArgumentException e) {
            getEngine().logger.warn("Illegal list of paths " + paths);
            throw new JavaException(e);
        } catch (Exception e) {
            throw new JavaException(e);
        }
    }

    /**
     * set the field value of an object
     */
    private boolean java_set(PTerm objId, PTerm fieldTerm, PTerm 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).getName().equals("class")) {
                String clName = null;
                // Case: class(className)
                if (((Struct) objId).getArity() == 1)
                    clName = alice.util.Tools.removeApices(((Struct) objId).getArg(0).toString());
                if (clName != null) {
                    try {
                        cl = Class.forName(clName, true, dynamicLoader);
                    } catch (ClassNotFoundException ex) {
                        getEngine().logger.warn("Java class not found: " + clName);
                        return false;
                    } catch (Exception ex) {
                        getEngine().logger.warn("Static field " + fieldName + " not found in class "
                                + alice.util.Tools.removeApices(((Struct) objId).getArg(0).toString()));
                        return false;
                    }
                }
            } else {
                String objName = alice.util.Tools.removeApices(objId.toString());
                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 = alice.util.Tools.removeApices(what.toString());
                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().logger.warn("Field " + fieldName + " not found in class " + objId);
            return false;
        } catch (Exception ex) {
            return false;
        }
    }

    /**
     * get the value of the field
     */
    private boolean java_get(PTerm objId, PTerm fieldTerm, PTerm what) {
        if (!fieldTerm.isAtom()) {
            return false;
        }
        String fieldName = ((Struct) fieldTerm).getName();
        Object obj = null;
        try {
            Class<?> cl = null;
            if (objId.isCompound() && ((Struct) objId).getName().equals("class")) {
                String clName = null;
                if (((Struct) objId).getArity() == 1)
                    clName = alice.util.Tools.removeApices(((Struct) objId).getArg(0).toString());
                if (clName != null) {
                    try {
                        cl = Class.forName(clName, true, dynamicLoader);
                    } catch (ClassNotFoundException ex) {
                        getEngine().logger.warn("Java class not found: " + clName);
                        return false;
                    } catch (Exception ex) {
                        getEngine().logger.warn("Static field " + fieldName + " not found in class "
                                + alice.util.Tools.removeApices(((Struct) objId).getArg(0).toString()));
                        return false;
                    }
                }
            } else {
                String objName = alice.util.Tools.removeApices(objId.toString());
                obj = currentObjects.get(objName);
                if (obj == null) {
                    return false;
                }
                cl = obj.getClass();
            }

            Field field = cl.getField(fieldName);
            Class<?> fc = field.getType();
            field.setAccessible(true);
            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 (NoSuchFieldException ex) {
            getEngine().logger.warn("Field " + fieldName + " not found in class " + objId);
            return false;
        } catch (Exception ex) {
            getEngine().logger.warn("Generic error in accessing the field");
            return false;
        }
    }

    public boolean java_array_set_primitive_3(PTerm obj_id, PTerm i, PTerm what) throws JavaException {
        Struct objId = (Struct) obj_id.getTerm();
        Number index = (Number) i.getTerm();
        what = what.getTerm();
        Object obj = null;
        if (!index.isInteger()) {
            throw new JavaException(new IllegalArgumentException(index.toString()));
        }
        try {
            Class<?> cl = null;
            String objName = alice.util.Tools.removeApices(objId.toString());
            obj = currentObjects.get(objName);
            if (obj != null) {
                cl = obj.getClass();
            } else {
                throw new JavaException(new IllegalArgumentException(objId.toString()));
            }

            if (!cl.isArray()) {
                throw new JavaException(new IllegalArgumentException(objId.toString()));
            }
            String name = cl.toString();
            switch (name) {
            case "class [I": {
                if (!(what instanceof Number)) {
                    throw new JavaException(new IllegalArgumentException(what.toString()));
                }
                byte v = (byte) ((Number) what).intValue();
                Array.setInt(obj, index.intValue(), v);
                break;
            }
            case "class [D": {
                if (!(what instanceof Number)) {
                    throw new JavaException(new IllegalArgumentException(what.toString()));
                }
                double v = ((Number) what).doubleValue();
                Array.setDouble(obj, index.intValue(), v);
                break;
            }
            case "class [F": {
                if (!(what instanceof Number)) {
                    throw new JavaException(new IllegalArgumentException(what.toString()));
                }
                float v = ((Number) what).floatValue();
                Array.setFloat(obj, index.intValue(), v);
                break;
            }
            case "class [L": {
                if (!(what instanceof Number)) {
                    throw new JavaException(new IllegalArgumentException(what.toString()));
                }
                long v = ((Number) what).longValue();
                Array.setFloat(obj, index.intValue(), v);
                break;
            }
            case "class [C": {
                String s = what.toString();
                Array.setChar(obj, index.intValue(), s.charAt(0));
                break;
            }
            case "class [Z": {
                String s = what.toString();
                switch (s) {
                case "true":
                    Array.setBoolean(obj, index.intValue(), true);
                    break;
                case "false":
                    Array.setBoolean(obj, index.intValue(), false);
                    break;
                default:
                    throw new JavaException(new IllegalArgumentException(what.toString()));
                }
                break;
            }
            case "class [B": {
                if (!(what instanceof Number)) {
                    throw new JavaException(new IllegalArgumentException(what.toString()));
                }
                int v = ((Number) what).intValue();
                Array.setByte(obj, index.intValue(), (byte) v);
                break;
            }
            case "class [S": {
                if (!(what instanceof Number)) {
                    throw new JavaException(new IllegalArgumentException(what.toString()));
                }
                short v = (short) ((Number) what).intValue();
                Array.setShort(obj, index.intValue(), v);
                break;
            }
            default:
                throw new JavaException(new Exception());
            }
            return true;
        } catch (Exception ex) {
            throw new JavaException(ex);
        }
    }

    /**
     * Sets the value of the field 'i' with 'what'
     * @param obj_id
     * @param i
     * @param what
     * @return
     * @throws JavaException
     */
    public boolean java_array_get_primitive_3(PTerm obj_id, PTerm i, PTerm what) throws JavaException {
        Struct objId = (Struct) obj_id.getTerm();
        Number index = (Number) i.getTerm();
        what = what.getTerm();
        Object obj = null;
        if (!index.isInteger()) {
            throw new JavaException(new IllegalArgumentException(index.toString()));
        }
        try {
            Class<?> cl = null;
            String objName = alice.util.Tools.removeApices(objId.toString());
            obj = currentObjects.get(objName);
            if (obj != null) {
                cl = obj.getClass();
            } else {
                throw new JavaException(new IllegalArgumentException(objId.toString()));
            }

            if (!cl.isArray()) {
                throw new JavaException(new IllegalArgumentException(objId.toString()));
            }
            String name = cl.toString();
            switch (name) {
            case "class [I": {
                PTerm value = new Int(Array.getInt(obj, index.intValue()));
                if (unify(what, value))
                    return true;
                else
                    throw new JavaException(new IllegalArgumentException(what.toString()));
            }
            case "class [D": {
                PTerm value = new alice.tuprolog.Double(Array.getDouble(obj, index.intValue()));
                if (unify(what, value))
                    return true;
                else
                    throw new JavaException(new IllegalArgumentException(what.toString()));
            }
            case "class [F": {
                PTerm value = new alice.tuprolog.Float(Array.getFloat(obj, index.intValue()));
                if (unify(what, value))
                    return true;
                else
                    throw new JavaException(new IllegalArgumentException(what.toString()));
            }
            case "class [L": {
                PTerm value = new alice.tuprolog.Long(Array.getLong(obj, index.intValue()));
                if (unify(what, value))
                    return true;
                else
                    throw new JavaException(new IllegalArgumentException(what.toString()));
            }
            case "class [C": {
                PTerm value = new Struct("" + Array.getChar(obj, index.intValue()));
                if (unify(what, value))
                    return true;
                else
                    throw new JavaException(new IllegalArgumentException(what.toString()));
            }
            case "class [Z":
                boolean b = Array.getBoolean(obj, index.intValue());
                if (b) {
                    if (unify(what, PTerm.TRUE))
                        return true;
                    else
                        throw new JavaException(new IllegalArgumentException(what.toString()));
                } else {
                    if (unify(what, PTerm.FALSE))
                        return true;
                    else
                        throw new JavaException(new IllegalArgumentException(what.toString()));
                }
            case "class [B": {
                PTerm value = new Int(Array.getByte(obj, index.intValue()));
                if (unify(what, value))
                    return true;
                else
                    throw new JavaException(new IllegalArgumentException(what.toString()));
            }
            case "class [S": {
                PTerm value = new Int(Array.getInt(obj, index.intValue()));
                if (unify(what, value))
                    return true;
                else
                    throw new JavaException(new IllegalArgumentException(what.toString()));
            }
            default:
                throw new JavaException(new Exception());
            }
        } catch (Exception ex) {
            // ex.printStackTrace();
            throw new JavaException(ex);
        }

    }

    private boolean java_array(String type, int nargs, PTerm id) {
        try {
            Object array = null;
            String obtype = type.substring(0, type.length() - 2);

            switch (obtype) {
            case "boolean":
                array = new boolean[nargs];
                break;
            case "byte":
                array = new byte[nargs];
                break;
            case "char":
                array = new char[nargs];
                break;
            case "short":
                array = new short[nargs];
                break;
            case "int":
                array = new int[nargs];
                break;
            case "long":
                array = new long[nargs];
                break;
            case "float":
                array = new float[nargs];
                break;
            case "double":
                array = new double[nargs];
                break;
            default:
                Class<?> cl = Class.forName(obtype, true, dynamicLoader);
                array = Array.newInstance(cl, nargs);
                break;
            }
            return bindDynamicObject(id, array);
        } catch (Exception ex) {
            // ex.printStackTrace();
            return false;
        }
    }

    /**
     * Returns an URL array from a String array
     *
     * @throws JavaException
     */
    private URL[] getURLsFromStringArray(String[] paths) throws MalformedURLException {
        URL[] urls = null;
        if (paths != null) {
            urls = new URL[paths.length];

            for (int i = 0; i < paths.length; i++) {
                if (paths[i] == null)
                    continue;
                if (paths[i].contains("http") || paths[i].contains("https") || paths[i].contains("ftp"))
                    urls[i] = new URL(paths[i]);
                else {
                    File file = new File(paths[i]);
                    urls[i] = (file.toURI().toURL());
                }
            }
        }
        return urls;
    }

    /**
     * Returns a String array from a Struct contains a list
     *
     * @throws JavaException
     */

    private String[] getStringArrayFromStruct(Struct list) {
        String args[] = new String[list.listSize()];
        Iterator<? extends PTerm> it = list.listIterator();
        int count = 0;
        while (it.hasNext()) {
            String path = alice.util.Tools.removeApices(it.next().toString());
            args[count++] = path;
        }
        return args;
    }

    /**
     * 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 (!parse_arg(values, types, i, 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 (!parse_arg(values, types, i, (PTerm) objs[i]))
                return null;
        }
        return new Signature(values, types);
    }

    private boolean parse_arg(Object[] values, Class<?>[] types, int i, PTerm term) {
        try {
            if (term == null) {
                values[i] = null;
                types[i] = null;
            } else if (term.isAtom()) {
                String name = alice.util.Tools.removeApices(term.toString());
                switch (name) {
                case "true":
                    values[i] = Boolean.TRUE;
                    types[i] = Boolean.TYPE;
                    break;
                case "false":
                    values[i] = Boolean.FALSE;
                    types[i] = Boolean.TYPE;
                    break;
                default:
                    Object obj = currentObjects.get(name);
                    values[i] = obj == null ? name : obj;
                    types[i] = values[i].getClass();
                    break;
                }
            } else if (term instanceof Number) {
                Number t = (Number) term;
                if (t instanceof Int) {
                    values[i] = t.intValue();
                    types[i] = java.lang.Integer.TYPE;
                } else if (t instanceof alice.tuprolog.Double) {
                    values[i] = t.doubleValue();
                    types[i] = java.lang.Double.TYPE;
                } else if (t instanceof alice.tuprolog.Long) {
                    values[i] = t.longValue();
                    types[i] = java.lang.Long.TYPE;
                } else if (t instanceof alice.tuprolog.Float) {
                    values[i] = 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 parse_as(values, types, i, tc.getTerm(0), tc.getTerm(1));
                } else {
                    Object obj = currentObjects.get(alice.util.Tools.removeApices(tc.toString()));
                    values[i] = obj == null ? alice.util.Tools.removeApices(tc.toString()) : 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 parse_as(Object[] values, Class<?>[] types, int i, PTerm castWhat, PTerm castTo) {
        try {
            if (!(castWhat instanceof Number)) {
                String castTo_name = alice.util.Tools.removeApices(((Struct) castTo).getName());
                String castWhat_name = alice.util.Tools.removeApices(castWhat.getTerm().toString());
                // 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("[]")) {
                    switch (castTo_name) {
                    case "boolean[]":
                        castTo_name = "[Z";
                        break;
                    case "byte[]":
                        castTo_name = "[B";
                        break;
                    case "short[]":
                        castTo_name = "[S";
                        break;
                    case "char[]":
                        castTo_name = "[C";
                        break;
                    case "int[]":
                        castTo_name = "[I";
                        break;
                    case "long[]":
                        castTo_name = "[L";
                        break;
                    case "float[]":
                        castTo_name = "[F";
                        break;
                    case "double[]":
                        castTo_name = "[D";
                        break;
                    default:
                        castTo_name = "[L" + castTo_name.substring(0, castTo_name.length() - 2) + ';';
                        break;
                    }
                }
                if (!castWhat_name.equals("null")) {
                    Object obj_to_cast = currentObjects.get(castWhat_name);
                    if (obj_to_cast == null) {
                        if (castTo_name.equals("boolean")) {
                            switch (castWhat_name) {
                            case "true":
                                values[i] = new Boolean(true);
                                break;
                            case "false":
                                values[i] = new Boolean(false);
                                break;
                            default:
                                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, true, dynamicLoader);
                        } catch (ClassNotFoundException ex) {
                            getEngine().logger.warn("Java class not found: " + castTo_name);
                            return false;
                        }
                    }
                } else {
                    values[i] = null;
                    switch (castTo_name) {
                    case "byte":
                        types[i] = Byte.TYPE;
                        break;
                    case "short":
                        types[i] = Short.TYPE;
                        break;
                    case "char":
                        types[i] = Character.TYPE;
                        break;
                    case "int":
                        types[i] = Integer.TYPE;
                        break;
                    case "long":
                        types[i] = java.lang.Long.TYPE;
                        break;
                    case "float":
                        types[i] = java.lang.Float.TYPE;
                        break;
                    case "double":
                        types[i] = java.lang.Double.TYPE;
                        break;
                    case "boolean":
                        types[i] = Boolean.TYPE;
                        break;
                    default:
                        try {
                            types[i] = Class.forName(castTo_name, true, dynamicLoader);
                        } catch (ClassNotFoundException ex) {
                            getEngine().logger.warn("Java class not found: " + castTo_name);
                            return false;
                        }
                        break;
                    }
                }
            } else {
                Number num = (Number) castWhat;
                String castTo_name = ((Struct) castTo).getName();
                switch (castTo_name) {
                case "byte":
                    values[i] = new Byte((byte) num.intValue());
                    types[i] = Byte.TYPE;
                    break;
                case "short":
                    values[i] = new Short((short) num.intValue());
                    types[i] = Short.TYPE;
                    break;
                case "int":
                    values[i] = new Integer(num.intValue());
                    types[i] = Integer.TYPE;
                    break;
                case "long":
                    values[i] = new java.lang.Long(num.longValue());
                    types[i] = java.lang.Long.TYPE;
                    break;
                case "float":
                    values[i] = new java.lang.Float(num.floatValue());
                    types[i] = java.lang.Float.TYPE;
                    break;
                case "double":
                    values[i] = new java.lang.Double(num.doubleValue());
                    types[i] = java.lang.Double.TYPE;
                    break;
                default:
                    return false;
                }
            }
        } catch (Exception ex) {
            getEngine().logger.warn("Casting " + castWhat + " to " + castTo + " failed");
            return false;
        }
        return true;
    }

    final static Logger logger = LoggerFactory.getLogger(OOLibrary.class);

    /**
     * parses return value of a method invokation
     */
    private boolean parseResult(PTerm id, Object obj) {
        if (obj == null) {
            // return unify(id,Term.TRUE);
            return unify(id, new Var());
        }
        try {
            if (Boolean.class.isInstance(obj)) {
                return (boolean) obj ? unify(id, PTerm.TRUE) : unify(id, PTerm.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));
            } else if (java.lang.Long.class.isInstance(obj)) {
                return unify(id, new alice.tuprolog.Long((java.lang.Long) obj));
            } else if (java.lang.Float.class.isInstance(obj)) {
                return unify(id, new alice.tuprolog.Float((java.lang.Float) obj));
            } else if (java.lang.Double.class.isInstance(obj)) {
                return unify(id, new alice.tuprolog.Double((java.lang.Double) obj));
            } else if (String.class.isInstance(obj)) {
                return unify(id, new Struct((String) obj));
            } else if (Character.class.isInstance(obj)) {
                return unify(id, new Struct(obj.toString()));
            } else {
                return bindDynamicObject(id, obj);
            }
        } catch (Exception ex) {
            // ex.printStackTrace();
            logger.info("parse {}", ex);
            return false;
        }
    }

    private Object[] getArrayFromList(Struct list) {
        Object args[] = new Object[list.listSize()];
        Iterator<? extends PTerm> it = list.listIterator();
        int count = 0;
        while (it.hasNext()) {
            args[count++] = it.next();
        }
        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.
     * 
     * 
     * @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 = staticObjects_inverse.get(obj);

            if (aKey != null) {
                // object already referenced
                return false;
            } else {
                String raw_name = alice.util.Tools.removeApices(id.getTerm().toString());
                staticObjects.put(raw_name, obj);
                staticObjects_inverse.put(obj, id);
                return true;
            }
        }
    }

    /**
     * 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
     *            
     * @return true if the operation is successful
     * @throws JavaException
     *             if the object id is not valid
     */
    public boolean register_1(PTerm id) throws JavaException {
        id = id.getTerm();
        Object obj = null;
        try {
            obj = getRegisteredDynamicObject((Struct) id);
            return register((Struct) id, obj);
        } catch (InvalidObjectIdException e) {
            getEngine().logger.warn("Illegal object id " + id.toString());
            throw new JavaException(e);
        }
    }

    /**
     * Unregister an object with the specified id.
     * 
     * The identifier must be a ground object.
     * 
     * @param id
     *            object identifier
     *            
     * @return true if the operation is successful
     * @throws JavaException
     *             if the object id is not valid
     */
    public boolean unregister_1(PTerm id) throws JavaException {
        id = id.getTerm();
        try {
            return unregister((Struct) id);
        } catch (InvalidObjectIdException e) {
            getEngine().logger.warn("Illegal object id " + id.toString());
            throw new JavaException(e);
        }
    }

    /**
     * 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) {
        // already registered object?
        synchronized (staticObjects) {
            Object aKey = staticObjects_inverse.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);
                staticObjects_inverse.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(alice.util.Tools.removeApices(id.toString()));
        }
    }

    /**
     * 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 = alice.util.Tools.removeApices(id.toString());
            Object obj = staticObjects.remove(raw_name);
            if (obj != null) {
                staticObjects_inverse.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 = alice.util.Tools.removeApices(id.toString());
            currentObjects.put(raw_name, obj);
            currentObjects_inverse.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 = currentObjects_inverse.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);
                currentObjects_inverse.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(alice.util.Tools.removeApices(id.toString()));
        }
    }

    /**
     * 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 = alice.util.Tools.removeApices(id.toString());
            Object obj = currentObjects.remove(raw_name);
            if (obj != null) {
                currentObjects_inverse.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(PTerm id, Object obj) {
        // null object are considered to _ variable
        if (obj == null) {
            return unify(id, new Var());
        }
        // already registered object?
        synchronized (currentObjects) {
            Object aKey = currentObjects_inverse.get(obj);
            if (aKey != null) {
                // object already referenced -> unifying terms
                // referencing the object
                // log("obj already registered: unify "+id+" "+aKey);
                return unify(id, (PTerm) 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 = alice.util.Tools.removeApices(id.getTerm().toString());
                    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++);
    }

    /**
     * handling writeObject method is necessary in order to make the library
     * serializable, 'nullyfing' eventually objects registered in maps
     */
    private void writeObject(java.io.ObjectOutputStream out) throws IOException {
        HashMap<String, Object> bak00 = currentObjects;
        IdentityHashMap<Object, Struct> bak01 = currentObjects_inverse;
        try {
            currentObjects = null;
            currentObjects_inverse = null;
            out.defaultWriteObject();
        } catch (IOException ex) {
            currentObjects = bak00;
            currentObjects_inverse = bak01;
            throw new IOException();
        }
        currentObjects = bak00;
        currentObjects_inverse = bak01;
    }

    /**
     * handling readObject method is necessary in order to have the library
     * reconstructed after a serialization
     */
    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        currentObjects = new HashMap<>();
        currentObjects_inverse = new IdentityHashMap<>();
        preregisterObjects();
    }

    // --------------------------------------------------

    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 args & no exact match, out of
                // luck
                return null;
            }
        }

        // go the more complicated route
        Method[] methods = target.getMethods();
        Vector<Method> goodMethods = new Vector<>();
        for (int i = 0; i != methods.length; i++) {
            if (name.equals(methods[i].getName()) && matchClasses(methods[i].getParameterTypes(), argClasses))
                goodMethods.addElement(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.firstElement();
        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();
        Vector<Constructor<?>> goodConstructors = new Vector<>();
        for (int i = 0; i != constructors.length; i++) {
            if (matchClasses(constructors[i].getParameterTypes(), argClasses))
                goodConstructors.addElement(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 method compatible
                    // 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.firstElement();
        default:
            return mostSpecificConstructor(goodConstructors);
        }
    }

    // 1st arg 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(Vector<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.elementAt(i), methods.elementAt(j)))) {
                    methods.removeElementAt(j);
                    if (i > j)
                        i--;
                    j--;
                }
            }
        }
        if (methods.size() == 1)
            return methods.elementAt(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(Vector<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.elementAt(i), constructors.elementAt(j)))) {
                    constructors.removeElementAt(j);
                    if (i > j)
                        i--;
                    j--;
                }
            }
        }
        if (constructors.size() == 1)
            return constructors.elementAt(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] = ((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] = (float) ((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] = ((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 mantains information about type and value of a method
 * arguments
 */
@SuppressWarnings("serial")
class Signature implements Serializable {
    Class<?>[] types;
    Object[] values;

    public Signature(Object[] v, Class<?>[] c) {
        values = v;
        types = c;
    }

    public Class<?>[] getTypes() {
        return types;
    }

    Object[] getValues() {
        return values;
    }

    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 {
}

/**
 * Information about an EventListener
 */
@SuppressWarnings("serial")
class ListenerInfo implements Serializable {
    public String listenerInterfaceName;
    public EventListener listener;
    // public String eventName;
    public String eventFullClass;

    public ListenerInfo(EventListener l, String eventClass, String n) {
        listener = l;
        // this.eventName=eventName;
        this.eventFullClass = eventClass;
        listenerInterfaceName = n;
    }
}