package abstrasy.interpreter;
import abstrasy.Heap;
import abstrasy.Interpreter;
import abstrasy.Node;
import abstrasy.PCoder;
import abstrasy.SELF;
import abstrasy.Tools;
import abstrasy.externals.AExtCachable;
import abstrasy.externals.annotations.ETK_hidden;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
/**
* Abstrasy Interpreter
*
* Copyright : Copyright (c) 2006-2012, Luc Bruninx.
*
* Concédée sous licence EUPL, version 1.1 uniquement (la «Licence»).
*
* Vous ne pouvez utiliser la présente oeuvre que conformément à la Licence.
* Vous pouvez obtenir une copie de la Licence à l’adresse suivante:
*
* http://www.osor.eu/eupl
*
* Sauf obligation légale ou contractuelle écrite, le logiciel distribué sous
* la Licence est distribué "en l’état", SANS GARANTIES OU CONDITIONS QUELLES
* QU’ELLES SOIENT, expresses ou implicites.
*
* Consultez la Licence pour les autorisations et les restrictions
* linguistiques spécifiques relevant de la Licence.
*
*
* @author Luc Bruninx
* @version 1.0
*/
public class ExternalTK {
public ExternalTK() {
}
/**
* EXTERNAL_ -> méthode externe native...
*/
private static final String EXTERNAL_ = "external_";
//
// externalpc_ -> méthode à exécuter systématiquement...
//
//private static final String externalpc_ = "externalpc_";
/**
* _EXTERNAL_CLASS_ -> masque de recherche des classes enfichables...
*/
public static final String _EXTERNAL_CLASS_ = ".External_";
private static final Node ROOTNODE = new Node();
/*
* N'est plus utilisé. On la conserve à titre de documentation...
*
*
private static final Node invokeE_(Object external, String methode, Object[] args) {
Node res = null;
Class<?> acls[] = (args != null ? new Class<?>[] { args.getClass() }: null);
Method meth = null;
try {
meth = external.getClass().getDeclaredMethod(methode, acls);
}
catch (Exception ex0) {
try {
meth = external.getClass().getMethod(methode, acls);
}
catch (Exception ex1) {
meth = null;
}
}
if (meth != null) {
Object obj;
try {
obj = meth.invoke(external, args);
}
catch (IllegalAccessException e) {
obj = null;
}
catch (InvocationTargetException e) {
obj = null;
}
if (obj != null) {
if (obj instanceof Node) {
res = (Node) obj;
}
else if (obj instanceof String) {
res = new Node((String) obj);
}
else {
res = Node.createExternal(obj);
}
}
}
return res;
}
*
*/
/**
* Envoyer le message décrit dans startAt à l'objet externe.
*
* Le premier élément (0) de startAt doit être un symbole quoté. Ce symbole fournit
* le nom de la méthode à appeler et les reste des éléments les éléments de argv.
*
* @param externalNode
* @param startAt
* @return
* @throws Exception
*/
public static Node evalMethod(Node externalNode, Node startAt) throws Exception {
externalNode.requireNodeType(Node.TYPE_EXTERNAL);
try {
Object eobject = externalNode.getExternal();
Object args[] = { startAt };
Class acls[] = { startAt.getClass() };
Method meth;
Node mnode = startAt.elementAt(0);
mnode.requireNodeType(Node.TYPE_QSYMBOL);
String mname = PCoder.unselfing(mnode.getSymbol().getStr());
try {
meth = eobject.getClass().getMethod(Name.toExternal(mname), acls);
}
catch (Exception ex1) {
ex1.printStackTrace();
meth = null;
}
if (meth == null)
throw new InterpreterException(StdErrors.extend(StdErrors.External_error, "not found " + mname + " : "+externalNode ));
Node old_self = SELF.swap(externalNode);
Object obj = meth.invoke(eobject, args);
SELF.restore(old_self);
if (obj != null)
if (obj instanceof Node)
return (Node) obj;
else
return Node.createExternal(obj);
else
return null;
}
catch (Exception ex) {
//System.out.println("***"+startAt);
if (ex instanceof InterpreterException)
throw ex;
else {
if (Interpreter.isDebugMode()) {
Interpreter.Log(ex.getCause().toString());
ex.printStackTrace();
}
throw new InterpreterException(StdErrors.extend(StdErrors.External_error, ex.getCause().toString()));
}
}
}
public static Node evalMethod_or_null(Node externalNode, Node startAt) throws Exception {
externalNode.requireNodeType(Node.TYPE_EXTERNAL);
Object eobject = externalNode.getExternal();
Object args[] = { startAt };
Class acls[] = { startAt.getClass() };
Method meth;
Node mnode = startAt.elementAt(0);
mnode.requireNodeType(Node.TYPE_QSYMBOL);
String mname = PCoder.unselfing(mnode.getSymbol().getStr());
try {
meth = eobject.getClass().getMethod(Name.toExternal(mname), acls);
}
catch (Exception ex1) {
return null;
}
Object obj = null;
try {
obj = meth.invoke(eobject, args);
}
catch (Exception ex) {
return null;
}
if (obj != null)
if (obj instanceof Node)
return (Node) obj;
else
return Node.createExternal(obj);
else
return null;
}
/**
* Fourni la liste des slots publiques accessibles à partir de l'interface de l'objet externe.
*
* @param externalNode
* @return
*/
public static final ArrayList<String> getExternalSlots(Node externalNode) {
ArrayList<String> abstrasyn = new ArrayList<String>();
Class<?> cl = externalNode.getExternal().getClass();
ArrayList<Method> mtv = new ArrayList<Method>();
lockupAllMethodsFrom(cl, mtv);
for (int imt = mtv.size() - 1; imt >= 0; ) {
Method mt = mtv.get(imt--);
String mtn = mt.getName();
if (mtn.startsWith(EXTERNAL_) && Modifier.isPublic(mt.getModifiers()) && mt.getReturnType().isInstance(ROOTNODE))
abstrasyn.add(Name.toAbstrasy(mtn));
}
return abstrasyn;
}
public static final boolean hasSlot(Node externalNode, String slotSymbol) {
Object e = externalNode.getExternal();
// s'il y a un cache...
if (e instanceof AExtCachable) {
String slotsStr = (String) ((AExtCachable) e).getSymbolsCache();
// si le cache est affecté...
if (slotsStr != null)
return slotsStr.indexOf("("+slotSymbol+")") >= 0;
else{
// si le cahce est vide...
ArrayList<String> slots = getExternalSlots(externalNode);
slotsStr="";
for (int i = slots.size() - 1; i >= 0; )
slotsStr+="("+slots.get(i--)+")";
((AExtCachable) e).setSymbolsCache(slotsStr);
return slotsStr.indexOf("("+slotSymbol+")") >= 0;
}
}
// en cas d'échec ou pas de cache...
ArrayList<String> slots = getExternalSlots(externalNode);
for (int i = slots.size() - 1; i >= 0; )
if (slots.get(i--).equals(slotSymbol))
return true;
return false;
}
/*
* Conservé en guise d'archive... N'est plus utilisée...
*
*
public static final void createExternalAdapter(Node vobjectNode, Node fromExternalNode) throws Exception {
//
// place les liens de fonctions de l'object externe dans le heap du VObject fourni en argument...
//
Heap heap = vobjectNode.getObjHeap();
heap.put(PCoder.EXTERNAL, fromExternalNode); // enregistrer sur le Heap...
Object obj = fromExternalNode.getExternal();
//
// Optimisation Cachable Heap ?
//
boolean cachable = obj instanceof AExtCachable;
ArrayList<InterfaceMapItem> heapCache = null;
String symbolsCache = null;
//
// si oui, on ira beaucoup plus vite en on consommera beaucoup moins de mémoire en réutilisant directement le cache s'il y en a un.
//
// Estimations: - 2 x plus rapide
// - Economie de mémoire importante en évitant les dupplications inutiles (on réutilise les mêmes définitions pour tous
// les objets.
///
if (cachable) {
AExtCachable cObj = (AExtCachable) obj;
heapCache = (ArrayList<InterfaceMapItem>) cObj.getHeapCache();
//
// si le cache existe, on l'utilise...
//
if (heapCache != null) {
for (int i = heapCache.size(); i > 0; ) {
InterfaceMapItem imitem = heapCache.get(--i);
String l = imitem.k;
Node n = imitem.v;
heap.put(l, Heap.isImmutable(l) ? n: n.deref_unsafe());
}
// c'est fini déjà...
return;
}
else {
//
// S'il n'y a pas de cache et que l'objet abstrait est cachable, on crée le cache...
///
heapCache = new ArrayList<InterfaceMapItem>();
symbolsCache = "";
}
}
Class<?> cl = obj.getClass();
ArrayList<Method> mtv = new ArrayList<Method>();
lockupAllMethodsFrom(cl, mtv);
Field[] dfl = cl.getDeclaredFields();
//for (int i = 0; i < dfl.length; i++) {
// System.out.println("field:" + dfl[i].toString());
//}
for (int imt = 0; imt < mtv.size(); imt++) {
Method mt = mtv.get(imt);
String mtn = mt.getName();
if (mtn.startsWith(external_)) {
if (Modifier.isPublic(mt.getModifiers())) {
if (mt.getReturnType().isInstance(ROOTNODE)) {
String abstrasyn = ExternalTK.abstrasyNameFromExternalMethod(external_, mtn);
symbolsCache += " " + abstrasyn + " ";
//
// symbole -> abstrasyn
//Node vdef = Node.createLazy();
// fonction appelante -> vdef
//vdef.append(PRECOMP.PC_EXTCALL_).append(PRECOMP.VS_SELF_EXTERNAL_).append(Node.createSymbol(mtn).letQuoted(true));
// hop dans le heap...
//Node fn_adapt = Node.createInlineFunction(vdef).letFinal(Heap.isImmutable(abstrasyn));
//heap.put(abstrasyn, fn_adapt);
//if (heapCache != null)
// heapCache.add(new InterfaceMapItem(abstrasyn, Heap.isImmutable(abstrasyn) ? fn_adapt: fn_adapt.deref()));
///
}
}
}
else if (mtn.startsWith(externalpc_)) {
if (Modifier.isPublic(mt.getModifiers())) {
if (mt.getReturnType().isInstance(ROOTNODE)) {
String abstrasyn = ExternalTK.abstrasyNameFromExternalMethod(externalpc_, mtn);
// symbole -> abstrasyn
Node vdef = invokeE_(obj, mtn, null);
//
// Si le Node est retourné, il est assigné dans le Heap à un symbole du nom de la
// méthode.
//
// Pour conserver le cache, il faut que le méthode retourne un Node et que son
// symbole soit immuable ou immutable (une constante).
///
if (vdef != null) {
// hop dans le heap...
heap.put(abstrasyn, vdef);
// si le Node est retourné, il est placé dans le cache...
if (heapCache != null)
heapCache.add(new InterfaceMapItem(abstrasyn, vdef));
}
else
heapCache = null; // ne pas pouvoir mettre ne cache dans ce cas...
if (!Heap.isImmutable(abstrasyn))
heapCache = null; // ne pas pouvoir mettre ne cache dans ce cas...
}
}
}
}
//
// S'il y a un cache de heap en préparation, on l'enregistre...
///
if (heapCache != null)
((AExtCachable) obj).setHeapCache(heapCache);
if (symbolsCache != null)
((AExtCachable) obj).setSymbolsCache(symbolsCache);
// c'est fini...
return;
}
*
*
*/
public static final void lockupAllMethodsFrom(Class<?> cl, ArrayList<Method> mtv) {
/**
*
* D'abord en profondeur:
* =====================
* Récupérer les méthodes à partir de la classe la plus générale possible.
**/
Class<?> cl2 = cl.getSuperclass();
if (cl2 != null) {
ExternalTK.lockupAllMethodsFrom(cl2, mtv);
}
/**
*
* Envisager les spécifications par la suite.
*
**/
// Récupérer les méthodes publiques
Method[] mta = cl.getMethods();
// pour toutes les méthodes récupérées
for (int i = 0; i < mta.length; i++) {
String tmp = mta[i].getName();
if (mta[i].isAnnotationPresent(ETK_hidden.class)) {
/*
* La méthode est annotée ETK_hidden.
* Si elle est listée, on la supprime.
*/
for (int j = 0; j < mtv.size(); j++) {
if (mtv.get(j).getName().equals(tmp)) {
mtv.remove(j);
j = mtv.size();
}
}
}
else {
/*
* Par défaut, on ajoute toutes les méthodes publiques.
* On vérifie toutefois de ne l'insérer qu'une seule fois dans la liste.
*/
boolean fnd = false;
for (int j = 0; j < mtv.size(); j++) {
if (mtv.get(j).getName().equals(tmp)) {
fnd = true;
j = mtv.size();
}
}
if (!fnd) {
mtv.add(mta[i]);
}
}
}
}
/*
* Retirée (l.bruninx, 2012-07-25)
*
public static String buildInExternalName(Node startAt, String extName) throws Exception {
String res = null;
try {
Package rootpack = startAt.getClass().getPackage();
//
// Class extclass = Class.forName(rootpack.getName() + _External_ + extName);
//
// remplacé par :
//
//
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class<?> extclass = loader.loadClass(rootpack.getName() + _EXTERNAL_CLASS_ + extName);
//
//
//
res = extclass.getName();
}
catch (Exception ex) {
if (Interpreter.isDebugMode()) {
Interpreter.Log(ex.getMessage());
ex.printStackTrace();
}
throw new InterpreterException(StdErrors.extend(StdErrors.External_error, "Can not load " + _EXTERNAL_CLASS_ + extName));
}
return res;
}
*
*/
public static Node makeExternal(String extName) throws Exception {
Node res = null;
try {
/**
* Class extclass = Class.forName(extName);
*
* remplacé par :
*
*/
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class<?> extclass = loader.loadClass(extName);
/**
*
*/
res = Node.createExternal(extclass.newInstance());
}
catch (Exception ex) {
if (ex instanceof SilentException) {
Interpreter.Log("SILENT EXCEPTION [makeExternal]");
throw new SilentException();
}
if (Interpreter.isDebugMode()) {
ex.printStackTrace();
}
throw new InterpreterException(StdErrors.External_error);
}
return res;
}
public static final class Name {
public static final String toAbstrasy(String externalName) {
String mn0 = externalName.substring(EXTERNAL_.length(), externalName.length());
mn0 = mn0.replace('_', '-');
mn0 = Tools.replaceCSeq(mn0, "---", "#"); // insérer inhibiteur...
mn0 = Tools.replaceCSeq(mn0, "--", "_");
if (mn0.startsWith("is-")) {
mn0 = mn0.substring(3, mn0.length()) + "?";
}
else if (mn0.startsWith("mutator-")) {
mn0 = mn0.substring(8, mn0.length()) + "!";
}
mn0 = Tools.replaceCSeq(mn0, "#", ""); // enlever inhibiteur...
return mn0;
}
public static final String toExternal(String abstrasyName) {
String mn0 = abstrasyName;
boolean is_ = mn0.charAt(mn0.length() - 1) == '?';
if (is_)
mn0 = mn0.substring(0, mn0.length() - 1);
boolean static_ = mn0.charAt(mn0.length() - 1) == '!';
if (static_)
mn0 = mn0.substring(0, mn0.length() - 1);
mn0 = Tools.replaceCSeq(mn0, "_", "--");
mn0 = mn0.replace('-', '_');
if (is_)
mn0 = "is_" + mn0;
if (static_)
mn0 = "mutator_" + mn0;
//System.out.println("*"+mn0+"*");
return EXTERNAL_ + mn0;
}
}
}