/******************************************************************************* * * Copyright (c) 2009 Fujitsu Services Ltd. * * Author: Nick Battle * * This file is part of VDMJ. * * VDMJ is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VDMJ is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with VDMJ. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ package org.overture.interpreter.util; import java.io.IOException; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; import org.overture.ast.definitions.AExplicitFunctionDefinition; import org.overture.ast.definitions.AExplicitOperationDefinition; import org.overture.ast.definitions.AImplicitFunctionDefinition; import org.overture.ast.definitions.AImplicitOperationDefinition; import org.overture.ast.definitions.PDefinition; import org.overture.ast.intf.lex.ILexNameToken; import org.overture.ast.lex.LexNameList; import org.overture.ast.messages.InternalException; import org.overture.ast.patterns.AIdentifierPattern; import org.overture.ast.patterns.PPattern; import org.overture.interpreter.runtime.Context; import org.overture.interpreter.runtime.ContextException; import org.overture.interpreter.runtime.ExitException; import org.overture.interpreter.values.Value; import org.overture.typechecker.assistant.ITypeCheckerAssistantFactory; public class Delegate implements Serializable { private static final long serialVersionUID = 1L; private final String name; private List<PDefinition> definitions; public Delegate(String name, List<PDefinition> definitions) { this.name = name; this.definitions = definitions; } private boolean delegateChecked = false; private Class<?> delegateClass = null; private Map<String, Method> delegateMethods = null; private Map<String, LexNameList> delegateArgs = null; public boolean hasDelegate(ITypeCheckerAssistantFactory assistantFactory) { if (!delegateChecked) { delegateChecked = true; try { String classname = name.replace('_', '.'); delegateClass = this.getClass().getClassLoader().loadClass(classname); delegateMethods = new HashMap<String, Method>(); delegateArgs = new HashMap<String, LexNameList>(); definitions = assistantFactory.createPDefinitionListAssistant().singleDefinitions(definitions); } catch (ClassNotFoundException e) { // Fine } } return delegateClass != null; } public Object newInstance() { try { return delegateClass.newInstance(); } catch (NullPointerException e) { throw new InternalException(63, "No delegate class found: " + name); } catch (InstantiationException e) { throw new InternalException(54, "Cannot instantiate native object: " + e.getMessage()); } catch (IllegalAccessException e) { throw new InternalException(55, "Cannot access native object: " + e.getMessage()); } } // gkanos:added parameters to pass the context as argument. private Method getDelegateMethod(String title, Context ctxt) { Method m = delegateMethods.get(title); if (m == null) { List<PPattern> plist = null; String mname = title.substring(0, title.indexOf('(')); // FIXME: this is to handle inheritance in the same way as VDMJ did. See CSV and IO, where the subclass // declared methods is in the tail of the list List<PDefinition> defs = new Vector<PDefinition>(definitions); Collections.reverse(defs); for (PDefinition d : defs) { if (d.getName().getName().equals(mname)) { if (ctxt.assistantFactory.createPDefinitionAssistant().isOperation(d)) { if (d instanceof AExplicitOperationDefinition) { AExplicitOperationDefinition e = (AExplicitOperationDefinition) d; plist = e.getParameterPatterns(); } else if (d instanceof AImplicitOperationDefinition) { AImplicitOperationDefinition e = (AImplicitOperationDefinition) d; plist = ctxt.assistantFactory.createAImplicitOperationDefinitionAssistant().getParamPatternList(e); } break; } else if (ctxt.assistantFactory.createPDefinitionAssistant().isFunction(d)) { if (d instanceof AExplicitFunctionDefinition) { AExplicitFunctionDefinition e = (AExplicitFunctionDefinition) d; plist = e.getParamPatternList().get(0); } else if (d instanceof AImplicitFunctionDefinition) { AImplicitFunctionDefinition e = (AImplicitFunctionDefinition) d; plist = ctxt.assistantFactory.createAImplicitFunctionDefinitionAssistant().getParamPatternList(e).get(0); } break; } } } LexNameList anames = new LexNameList(); List<Class<?>> ptypes = new Vector<Class<?>>(); if (plist != null) { for (PPattern p : plist) { if (p instanceof AIdentifierPattern) { AIdentifierPattern ip = (AIdentifierPattern) p; anames.add(ip.getName()); ptypes.add(Value.class); } else { throw new InternalException(56, "Native method cannot use pattern arguments: " + title); } } delegateArgs.put(title, anames); } else { throw new InternalException(57, "Native member not found: " + title); } try { Class<?>[] array = new Class<?>[0]; m = delegateClass.getMethod(mname, ptypes.toArray(array)); if (!m.getReturnType().equals(Value.class)) { throw new InternalException(58, "Native method does not return Value: " + m); } } catch (SecurityException e) { throw new InternalException(60, "Cannot access native method: " + e.getMessage()); } catch (NoSuchMethodException e) { throw new InternalException(61, "Cannot find native method: " + e.getMessage()); } delegateMethods.put(title, m); } return m; } public Value invokeDelegate(Object delegateObject, Context ctxt) { Method m = getDelegateMethod(ctxt.title, ctxt); if ((m.getModifiers() & Modifier.STATIC) == 0 && delegateObject == null) { throw new InternalException(64, "Native method should be static: " + m.getName()); } LexNameList anames = delegateArgs.get(ctxt.title); Object[] avals = new Object[anames.size()]; int a = 0; for (ILexNameToken arg : anames) { avals[a++] = ctxt.get(arg); } try { return (Value) m.invoke(delegateObject, avals); } catch (IllegalArgumentException e) { throw new InternalException(62, "Cannot invoke native method: " + e.getMessage()); } catch (IllegalAccessException e) { throw new InternalException(62, "Cannot invoke native method: " + e.getMessage()); } catch (InvocationTargetException e) { if (e.getTargetException() instanceof ExitException) { throw (ExitException) e.getTargetException(); } if (e.getTargetException() instanceof ContextException) { throw (ContextException) e.getTargetException(); } // if(e.getTargetException() instanceof ValueException) // { // throw (ValueException)e.getTargetException(); // } throw new InternalException(59, "Failed in native method: " + e.getTargetException().getMessage()); } } /** * The Method objects in the delegateMethods map cannot be serialized, which means that deep copies fail. So here, * we clear the map when serialization occurs. The map is re-build later on demand. */ private void writeObject(java.io.ObjectOutputStream out) throws IOException { if (delegateMethods != null) { delegateMethods.clear(); } out.defaultWriteObject(); } }