summaryrefslogtreecommitdiff
path: root/src/main/java/io/devnulllabs/openjava/mop/OJMethod.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/io/devnulllabs/openjava/mop/OJMethod.java')
-rw-r--r--src/main/java/io/devnulllabs/openjava/mop/OJMethod.java721
1 files changed, 721 insertions, 0 deletions
diff --git a/src/main/java/io/devnulllabs/openjava/mop/OJMethod.java b/src/main/java/io/devnulllabs/openjava/mop/OJMethod.java
new file mode 100644
index 0000000..45e5547
--- /dev/null
+++ b/src/main/java/io/devnulllabs/openjava/mop/OJMethod.java
@@ -0,0 +1,721 @@
+/*
+ * OJMethod.java
+ *
+ * Jul 28, 1998 by mich
+ */
+package io.devnulllabs.openjava.mop;
+
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Hashtable;
+
+import io.devnulllabs.openjava.ptree.ExpressionList;
+import io.devnulllabs.openjava.ptree.MethodDeclaration;
+import io.devnulllabs.openjava.ptree.ModifierList;
+import io.devnulllabs.openjava.ptree.ParameterList;
+import io.devnulllabs.openjava.ptree.ParseTree;
+import io.devnulllabs.openjava.ptree.StatementList;
+import io.devnulllabs.openjava.ptree.TypeName;
+import io.devnulllabs.openjava.ptree.Variable;
+
+
+public final class OJMethod implements OJMember
+{
+ private OJMethodImp substance;
+
+ private static Hashtable table = new Hashtable();
+
+ OJMethod( Method m ) {
+ this.substance = new OJMethodByteCode( m );
+ }
+
+ /**
+ * Constructs a new <code>OJMethod</code> object.
+ * <p>
+ * This constructor automatically generates parameter variables.
+ */
+ public OJMethod( OJClass declarer, OJModifier modif, OJClass returnType,
+ String name, OJClass[] parameterTypes,
+ OJClass[] exceptionTypes, StatementList body )
+ {
+ this( declarer, modif, returnType, name,
+ Toolbox.generateParameters( parameterTypes ),
+ exceptionTypes, body );
+ }
+
+ /**
+ * Constructs a new <code>OJMethod</code> object.
+ */
+ public OJMethod( OJClass declarer, OJModifier modif, OJClass returnType,
+ String name, OJClass[] parameterTypes,
+ String[] parameterNames,
+ OJClass[] exceptionTypes, StatementList body )
+ {
+ this( declarer, modif, returnType, name,
+ Toolbox.generateParameters( parameterTypes, parameterNames ),
+ exceptionTypes, body );
+ }
+
+ /**
+ * Constructs a new <code>OJMethod</code> object.
+ */
+ public OJMethod( OJClass declarer, OJModifier modif, OJClass returnType,
+ String name, ParameterList params,
+ OJClass[] exceptionTypes, StatementList body )
+ {
+ Environment env = declarer.getEnvironment();
+ ModifierList modiflist = new ModifierList();
+ modiflist.add( modif.toModifier() );
+ MethodDeclaration d = new MethodDeclaration(
+ modiflist,
+ TypeName.forOJClass( returnType ),
+ name,
+ params,
+ Toolbox.TNsForOJClasses( exceptionTypes ),
+ body
+ );
+ this.substance = new OJMethodSourceCode( env, declarer, d );
+ }
+
+ public OJMethod( Environment env, OJClass declarer, MethodDeclaration d ) {
+ this.substance = new OJMethodSourceCode( env, declarer, d );
+ }
+
+ /**
+ * Generates a method object which has the same attributes as the
+ * model method except its body.
+ * <p>
+ * The body of generated method is to be set to null.
+ *
+ * @param original the base model for generating method object.
+ */
+ public static OJMethod makePrototype( OJMethod original ) {
+ return new OJMethod( original.getDeclaringClass(),
+ original.getModifiers(),
+ original.getReturnType(),
+ original.getName(),
+ original.getParameterTypes(),
+ original.getExceptionTypes(),
+ null
+ );
+ }
+
+ public static OJMethod forMethod( Method java_method ) {
+ if (java_method == null) return null;
+ OJMethod method = (OJMethod) table.get( java_method );
+ if (method == null) {
+ method = new OJMethod( java_method );
+ table.put( java_method, method );
+ }
+ return method;
+ }
+
+ public static OJMethod[] arrayForMethods( Method[] jmethods ) {
+ OJMethod[] result = new OJMethod[jmethods.length];
+ for (int i = 0; i < result.length; ++i) {
+ result[i] = forMethod( jmethods[i] );
+ }
+ return result;
+ }
+
+ public Signature signature() {
+ return new Signature( this );
+ }
+
+ public OJClass getDeclaringClass() {
+ return substance.getDeclaringClass();
+ }
+
+ public String getName() {
+ return substance.getName();
+ }
+
+ public String getIdentifiableName() {
+ return substance.getIdentifiableName();
+ }
+
+ public OJModifier getModifiers() {
+ return substance.getModifiers();
+ }
+
+ public OJClass getReturnType() {
+ return substance.getReturnType();
+ }
+
+ public OJClass[] getParameterTypes() {
+ return substance.getParameterTypes();
+ }
+
+ public OJClass[] getExceptionTypes() {
+ return substance.getExceptionTypes();
+ }
+
+ /******************/
+ public ExpressionList getParameterVariables()
+ throws CannotAlterException
+ {
+ MethodDeclaration d = getSourceCode();
+ ParameterList params = d.getParameters();
+ ExpressionList result = new ExpressionList();
+ for (int i = 0, len = params.size(); i < len; ++i) {
+ result.add( new Variable( params.get( i ).getVariable() ) );
+ }
+ return result;
+ }
+
+ public String[] getParameters()
+ throws CannotAlterException
+ {
+ MethodDeclaration d = getSourceCode();
+ ParameterList params = d.getParameters();
+ String[] result = new String[params.size()];
+ for (int i = 0; i < result.length; ++i) {
+ result[i] = params.get( i ).getVariable().toString();
+ }
+ return result;
+ }
+
+ /**
+ * Obtains an parse tree of suffix in extended syntax starting
+ * with the specified keyword. Returned
+ * <code>io.devnulllabs.openjava.ptree.ParseTree</code> object has a structure
+ * built by an <code>io.devnulllabs.openjava.syntax.SyntaxRule</code> object
+ * returned via the method <code>getDeclSuffixRule(String)</code>.
+ *
+ * @see io.devnulllabs.openjava.mop.OJClass#getDeclSuffixRule(String)
+ * @see io.devnulllabs.openjava.syntax.SyntaxRule
+ *
+ * @return the parse tree
+ */
+ public ParseTree getSuffix( String keyword ) {
+ return substance.getSuffix( keyword );
+ }
+
+ /**
+ * Compares this method against the given object.
+ * The algorithm is borrowed by java.lang.reflect.Method.equals().
+ *
+ * @see java.lang.reflect.Method#equals
+ */
+ public boolean equals(Object obj) {
+ if (obj != null && obj instanceof OJMethod) {
+ OJMethod other = (OJMethod) obj;
+ return (getDeclaringClass() == other.getDeclaringClass())
+ && (getName().equals( other.getName() ))
+ && compareParameters( other );
+ }
+
+ return false;
+ }
+
+ private boolean compareParameters( OJMethod other ) {
+ return compareParameters( other.getParameterTypes() );
+ }
+
+ private boolean compareParameters( OJClass[] params2 ) {
+ OJClass[] params1 = getParameterTypes();
+ if (params1.length != params2.length) return false;
+ for (int i = 0; i < params1.length; ++i) {
+ if(params1[i] != params2[i]) return false;
+ }
+ return true;
+ }
+
+ /**
+ * Computes a hashcode for this method. The algorithm is borrowed
+ * by java.lang.reflect.Method.hashCode().
+ *
+ * @see java.lang.reflect.Method#hashCode
+ */
+ public int hashCode() {
+ return toString().hashCode();
+ }
+
+ public String toString() {
+ return substance.toString();
+ }
+
+ public Environment getEnvironment() {
+ return substance.getEnvironment();
+ }
+
+ /**
+ * Invokes this method on the given object with the given parameters.
+ *
+ * @exception CannotExecuteException if this method is not compiled yet.
+ */
+ public Object invoke( Object obj, Object[] args )
+ throws IllegalAccessException, IllegalArgumentException,
+ InvocationTargetException, CannotExecuteException
+ {
+ return substance.invoke( obj, args );
+ }
+
+ /* -- methods java.lang.reflect.Method does not supply. -- */
+
+ public final boolean isExecutable() {
+ return substance.isExecutable();
+ }
+
+ public final boolean isAlterable() {
+ return substance.isAlterable();
+ }
+
+ public final Method getByteCode() throws CannotExecuteException {
+ return substance.getByteCode();
+ }
+
+ public final MethodDeclaration getSourceCode()
+ throws CannotAlterException
+ {
+ return substance.getSourceCode();
+ }
+
+ public final StatementList getBody() throws CannotAlterException {
+ return substance.getBody();
+ }
+
+ /* -- inner use only -- */
+
+ void setDeclaringClass( OJClass parent ) throws CannotAlterException {
+ substance.setDeclaringClass( parent );
+ }
+
+ /* -- Translation (not overridable) -- */
+
+ public final void setName( String name ) throws CannotAlterException {
+ substance.setName( name );
+ }
+
+ public final void setModifiers( int mods ) throws CannotAlterException {
+ substance.setModifiers( mods );
+ }
+
+ public final void setModifiers( OJModifier mods )
+ throws CannotAlterException
+ {
+ setModifiers( mods.toModifier() );
+ }
+
+ public final void setReturnType( OJClass type )
+ throws CannotAlterException
+ {
+ substance.setReturnType( type );
+ }
+
+ public final void setExceptionTypes( OJClass[] types )
+ throws CannotAlterException
+ {
+ substance.setExceptionTypes( types );
+ }
+
+ public final void addExceptionType( OJClass type )
+ throws CannotAlterException
+ {
+ OJClass[] etypes = getExceptionTypes();
+ OJClass[] result = new OJClass[etypes.length + 1];
+ System.arraycopy( etypes, 0, result, 0, etypes.length );
+ result[etypes.length] = type;
+ setExceptionTypes( result );
+ }
+
+ public final StatementList setBody( StatementList stmts )
+ throws CannotAlterException
+ {
+ return substance.setBody( stmts );
+ }
+
+}
+
+
+/**
+ * The abstract class <code>OJMethodImp</code> provides an interface to
+ * an implementation of OJMethod.
+ */
+abstract class OJMethodImp
+{
+ public abstract String toString();
+ abstract OJClass getDeclaringClass();
+ abstract String getName();
+ abstract String getIdentifiableName();
+ abstract OJModifier getModifiers();
+ abstract OJClass getReturnType();
+ abstract OJClass[] getParameterTypes();
+ abstract OJClass[] getExceptionTypes();
+ abstract ParseTree getSuffix( String keyword );
+
+ abstract Environment getEnvironment();
+
+ abstract Object invoke( Object obj, Object[] args )
+ throws IllegalAccessException, IllegalArgumentException,
+ InvocationTargetException, CannotExecuteException;
+
+ /* -- methods java.lang.reflect.Method does not supply. -- */
+
+ abstract boolean isAlterable();
+ abstract boolean isExecutable();
+ abstract Method getByteCode() throws CannotExecuteException;
+ abstract MethodDeclaration getSourceCode() throws CannotAlterException;
+ abstract StatementList getBody() throws CannotAlterException;
+
+ /* -- inner use only -- */
+
+ abstract void setDeclaringClass( OJClass parent )
+ throws CannotAlterException;
+
+ /* -- Translation (not overridable) -- */
+
+ abstract void setName( String name ) throws CannotAlterException;
+ abstract void setModifiers( int mods ) throws CannotAlterException;
+ abstract void setReturnType( OJClass type ) throws CannotAlterException;
+ abstract void setExceptionTypes( OJClass[] types )
+ throws CannotAlterException;
+ abstract StatementList setBody( StatementList stmts )
+ throws CannotAlterException;
+
+}
+
+
+class OJMethodByteCode extends OJMethodImp
+{
+
+ private Method javaMethod = null;
+
+ OJMethodByteCode( Method java_method ) {
+ this.javaMethod = java_method;
+ }
+
+ public String toString() {
+ return javaMethod.toString();
+ }
+
+ OJClass getDeclaringClass() {
+ return OJClass.forClass( javaMethod.getDeclaringClass() );
+ }
+
+ String getName() {
+ return javaMethod.getName();
+ }
+
+ String getIdentifiableName() {
+ /***********/
+ return getDeclaringClass().getName() + "." + getName() + "()";
+ }
+
+ OJModifier getModifiers() {
+ return OJModifier.forModifier( javaMethod.getModifiers() );
+ }
+
+ OJClass getReturnType() {
+ return OJClass.forClass( javaMethod.getReturnType() );
+ }
+
+ OJClass[] getParameterTypes() {
+ return OJClass.arrayForClasses( javaMethod.getParameterTypes() );
+ }
+
+ OJClass[] getExceptionTypes() {
+ return OJClass.arrayForClasses( javaMethod.getExceptionTypes() );
+ }
+
+ ParseTree getSuffix( String keyword ) {
+ return null;
+ }
+
+ Environment getEnvironment() {
+ Environment result
+ = new ClosedEnvironment( getDeclaringClass().getEnvironment() );
+ return result;
+ }
+
+ Object invoke( Object obj, Object[] args )
+ throws IllegalAccessException, IllegalArgumentException,
+ InvocationTargetException, CannotExecuteException
+ {
+ return javaMethod.invoke( obj, args );
+ }
+
+ /* -- methods java.lang.reflect.Method does not supply. -- */
+
+ boolean isAlterable() {
+ return false;
+ }
+
+ boolean isExecutable() {
+ return true;
+ }
+
+ Method getByteCode() throws CannotExecuteException {
+ return javaMethod;
+ }
+
+ MethodDeclaration getSourceCode() throws CannotAlterException {
+ throw new CannotAlterException( "getSourceCode()" );
+ }
+
+ StatementList getBody() throws CannotAlterException {
+ throw new CannotAlterException( "getBody()" );
+ }
+
+ /* -- inner use only -- */
+
+ void setDeclaringClass( OJClass parent ) throws CannotAlterException {
+ throw new CannotAlterException( "setDeclaringClass()" );
+ }
+
+ /* -- Translation (not overridable) -- */
+
+ final void setName( String name ) throws CannotAlterException {
+ throw new CannotAlterException( "setName()" );
+ }
+
+ final void setModifiers( int mods ) throws CannotAlterException {
+ throw new CannotAlterException( "setModifiers()" );
+ }
+
+ final void setReturnType( OJClass type ) throws CannotAlterException {
+ throw new CannotAlterException( "setReturnType()" );
+ }
+
+ final void setExceptionTypes( OJClass[] types )
+ throws CannotAlterException
+ {
+ throw new CannotAlterException( "setExceptionTypes()" );
+ }
+
+ StatementList setBody( StatementList stmts )
+ throws CannotAlterException
+ {
+ throw new CannotAlterException( "setBody()" );
+ }
+
+}
+
+
+class OJMethodSourceCode extends OJMethodImp
+{
+
+ private static int idCounter = 0;
+ private int id;
+
+ private OJClass declarer;
+ private MethodDeclaration definition;
+ private Environment env;
+
+ OJMethodSourceCode( Environment env, OJClass declarer,
+ MethodDeclaration ptree )
+ {
+ this.declarer = declarer;
+ this.definition = ptree;
+ this.env = env;
+ this.id = idCounter++;
+ }
+
+ public String toString() {
+ OJClass declarer = getDeclaringClass();
+ String declarername = (declarer == null) ? "*" : declarer.getName();
+
+ StringBuffer buf = new StringBuffer();
+ String modif = getModifiers().toString();
+ if (! modif.equals( "" )) {
+ buf.append( modif );
+ buf.append( " " );
+ }
+ buf.append( getReturnType().getName() );
+ buf.append( " " );
+ buf.append( declarername );
+ buf.append( "." );
+ buf.append( getName() );
+ buf.append( "(" );
+ OJClass[] paramtypes = getParameterTypes();
+ if (paramtypes.length != 0) {
+ buf.append( paramtypes[0].getName() );
+ }
+ for (int i = 1; i < paramtypes.length; ++i) {
+ buf.append( "," );
+ buf.append( paramtypes[i].getName() );
+ }
+ buf.append( ")" );
+ return buf.toString();
+ }
+
+ OJClass getDeclaringClass() {
+ return this.declarer;
+ }
+
+ String getName() {
+ return definition.getName();
+ }
+
+ String getIdentifiableName() {
+ OJClass declarer = getDeclaringClass();
+ String declarername;
+ if (declarer == null) {
+ declarername = "*" + id;
+ } else {
+ declarername = declarer.getName();
+ }
+ /***************/
+ return declarername + "." + getName() + "()";
+ }
+
+ OJModifier getModifiers() {
+ return OJModifier.forParseTree( definition.getModifiers() );
+ }
+
+ OJClass getReturnType() {
+ String type_name = definition.getReturnType().toString();
+ return Toolbox.forNameAnyway( env, type_name );
+ }
+
+ private OJClass[] ptypeCache = null;
+ private TypeName[] paramtypes = null;
+ private boolean isPtypeCacheDirty(TypeName[] paramtypes) {
+ if (ptypeCache == null) return true;
+ if (paramtypes.length != this.paramtypes.length) return true;
+ for (int i = 0; i < paramtypes.length; ++i) {
+ if (! paramtypes[i].equals(this.paramtypes[i])) return true;
+ }
+ return false;
+ }
+ private void refleshPtypeCache() {
+ ParameterList plist = definition.getParameters();
+ int psize = plist.size();
+ TypeName[] paramtypes = new TypeName[psize];
+ for (int i = 0; i < psize; ++i) {
+ paramtypes[i] = plist.get(i).getTypeSpecifier();
+ }
+ if (isPtypeCacheDirty(paramtypes)) {
+ ptypeCache = arrayForParameters(plist);
+ this.paramtypes = paramtypes;
+ }
+ }
+
+ /*
+ * This method was:
+ * return arrayForParameters(definition.getParameters());
+ * but is tuned up for time efficiency.
+ */
+ OJClass[] getParameterTypes() {
+ refleshPtypeCache();
+ OJClass[] result = new OJClass[ptypeCache.length];
+ for (int i = 0; i < result.length; ++i) result[i] = ptypeCache[i];
+ return result;
+ }
+
+ String[] getParameters() {
+ ParameterList params = definition.getParameters();
+ String[] result = new String[params.size()];
+ for (int i = 0; i < result.length; ++i) {
+ result[i] = params.get( i ).getVariable().toString();
+ }
+ return result;
+ }
+
+ OJClass[] getExceptionTypes() {
+ return arrayForTypeNames( definition.getThrows() );
+ }
+
+ ParseTree getSuffix( String keyword ) {
+ Hashtable table = definition.getSuffixes();
+ if (table == null) return null;
+ return (ParseTree) table.get( keyword );
+ }
+
+ private final OJClass[] arrayForParameters( ParameterList params ) {
+ OJClass[] result = new OJClass[params.size()];
+ for (int i = 0; i < result.length; ++i) {
+ String tname = params.get( i ).getTypeSpecifier().toString();
+ result[i] = Toolbox.forNameAnyway( env, tname );
+ }
+ return result;
+ }
+
+ private final OJClass[] arrayForTypeNames( TypeName[] typenames ) {
+ OJClass[] result = new OJClass[typenames.length];
+ for (int i = 0; i < result.length; ++i) {
+ String tname = typenames[i].toString();
+ result[i] = Toolbox.forNameAnyway( env, tname );
+ }
+ return result;
+ }
+
+ Environment getEnvironment() {
+ Environment result
+ = new ClosedEnvironment( getDeclaringClass().getEnvironment() );
+ OJClass[] ptypes = getParameterTypes();
+ String[] pvars = getParameters();
+ for (int i = 0; i < ptypes.length; ++i) {
+ result.bindVariable( pvars[i], ptypes[i] );
+ }
+ return result;
+ }
+
+ /**
+ * Invokes this method on the given object with the given parameters.
+ *
+ * @exception CannotExecuteException if this method is not compiled yet.
+ */
+ Object invoke( Object obj, Object[] args )
+ throws IllegalAccessException, IllegalArgumentException,
+ InvocationTargetException, CannotExecuteException
+ {
+ throw new CannotExecuteException( "invoke()" );
+ }
+
+ /* -- methods java.lang.reflect.Method does not supply. -- */
+
+ boolean isAlterable() {
+ return true;
+ }
+
+ boolean isExecutable() {
+ return false;
+ }
+
+ Method getByteCode() throws CannotExecuteException {
+ throw new CannotExecuteException( "getByteCode()" );
+ }
+
+ MethodDeclaration getSourceCode() throws CannotAlterException {
+ return definition;
+ }
+
+ StatementList getBody() throws CannotAlterException {
+ return definition.getBody();
+ }
+
+ /* -- inner use only -- */
+
+ void setDeclaringClass( OJClass parent ) throws CannotAlterException {
+ this.declarer = parent;
+ }
+
+ /* -- Translation (not overridable) -- */
+
+ final void setName( String name ) throws CannotAlterException {
+ definition.setName( name );
+ }
+
+ final void setModifiers( int mods ) throws CannotAlterException {
+ definition.setModifiers( new ModifierList( mods ) );
+ }
+
+ final void setReturnType( OJClass type ) throws CannotAlterException {
+ definition.setReturnType( TypeName.forOJClass( type ) );
+ }
+
+ final void setExceptionTypes( OJClass[] types )
+ throws CannotAlterException
+ {
+ definition.setThrows( Toolbox.TNsForOJClasses( types ) );
+ }
+
+ StatementList setBody( StatementList stmts ) throws CannotAlterException {
+ StatementList result = definition.getBody();
+ definition.setBody( stmts );
+ return result;
+ }
+
+}