View Javadoc

1   ////////////////////////////////////////////////////////////////////////////////
2   // MillScript: an Open Spice interpreter and batch website creation tool
3   // Copyright (C) 2001-2004 Open World Ltd
4   // Copyright (C) 2005 Kevin Rogers
5   //
6   // This file is part of MillScript.
7   //
8   // MillScript is free software; you can redistribute it and/or modify it under
9   // the terms of the GNU General Public License as published by the Free
10  // Software Foundation; either version 2 of the License, or (at your option)
11  // any later version.
12  //
13  // MillScript is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
16  // more details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // MillScript; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA  02111-1307  USA
21  ////////////////////////////////////////////////////////////////////////////////
22  package org.millscript.millscript.functions;
23  
24  import org.millscript.commons.alert.Alert;
25  import org.millscript.millscript.alert.Alerts;
26  import org.millscript.millscript.vm.Machine;
27  
28  import java.lang.reflect.InvocationTargetException;
29  import java.lang.reflect.Method;
30  import java.lang.reflect.Modifier;
31  
32  /**
33   * This class implements a function which invokes a Java method.
34   */
35  public abstract class JMethod extends Function {
36  
37      /**
38       * The Java class this method belongs to.
39       */
40      private final Class clss;
41  
42      /**
43       * The Java method to invoke.
44       */
45      final Method method;
46  
47      /**
48       * The type of the parameters this method requires.
49       */
50      private final Class[] pTypes;
51  
52      /**
53       * The number of arguments the Java method requires.
54       */
55      private final int numArgs;
56  
57      /**
58       * A buffer to use for storing the arguments prior to invoking the
59       * underlying method.
60       */
61      final Object[] argBuffer;
62  
63      /**
64       * The number of arguments this function requires, which depends on whether
65       * the underlying Java method is static or not.
66       */
67      private final int required;
68  
69      /**
70       * Constructs a new <code>JMethod</code> function to invoke the specified
71       * Java method.
72       *
73       * @param m the Java method to invoke
74       */
75      public JMethod( final Method m ) {
76          this.clss = m.getDeclaringClass();
77          this.method = m;
78          pTypes = method.getParameterTypes();
79          numArgs = pTypes.length;
80          argBuffer = new Object[ numArgs ];
81  
82          int mods = method.getModifiers();
83          required = numArgs + ( Modifier.isStatic( mods ) ? 0 : 1 );
84      }
85  
86      /**
87       * @see org.millscript.millscript.functions.Function#apply(org.millscript.millscript.vm.Machine, int)
88       */
89      @Override
90      public void apply( final Machine mc, final int nargs ) {
91          checkNargs( mc, required, nargs );
92          mc.popArgsIntoArray( argBuffer );
93          try {
94              invoke( mc );
95          } catch ( IllegalAccessException ex ) {
96              throw(
97                  Alerts.eval(
98                      "Illegal access",
99                      "Java Function raised an access exception"
100                 ).culprit( "exception details", ex.getMessage() ).mishap()
101             );
102         } catch ( IllegalArgumentException ex ) {
103             for ( int i = numArgs - 1; i >= 0; i-- ) {
104                 final Object arg = argBuffer[ i ];
105                 if ( !pTypes[i].isInstance( arg ) ) {
106                     throw(
107                         Alerts.eval(
108                             "Unexpected type passed to Java Function",
109                             null
110                         ).
111                         culprit( "expected type", pTypes[i].getName() ).
112                         culprit( "actual type", arg.getClass() ).
113                         culprit( "actual value", arg ).
114                         mishap()
115                     );
116                 }
117             }
118             throw(
119                 Alerts.fault( "Cannot diagnose this IllegalAccessException" ).mishap()
120             );
121         } catch ( InvocationTargetException ex ) {
122             final Throwable cause = ex.getCause();
123             if ( cause instanceof NoClassDefFoundError ) {
124                 throw(
125                     Alerts.fault( "Invocation target" ).culprit( "Missing class", cause.getMessage() ).mishap()
126                 );
127             } else if ( cause instanceof Alert ) {
128                 throw (Alert) cause;
129             } else {
130                 throw(
131                     Alerts.eval(
132                         "The invoked Java method raised an exception",
133                         null
134                     ).culprit( "exception", cause ).mishap()
135                 );
136             }
137         }
138     }
139 
140     /**
141      * Invokes the underlying Java method in the appropriate manner using the
142      * specified machine.
143      *
144      * @param mc    the machine to perform the method on
145      * @throws IllegalAccessException   thrown if the underlying Java method
146      * cannot be accessed
147      * @throws InvocationTargetException    thrown if the underlying Java
148      * method throws an exception
149      */
150     abstract void invoke( final Machine mc ) throws IllegalAccessException, InvocationTargetException;
151 
152     /**
153      * Utility method returning a <code>Function</code> to invoke the specified
154      * Java method. This method checks the parameter types and modifiers of the
155      * supplied Java method and returns the appropriate <code>JMethod</code>.
156      *
157      * @param method    the method to invoke
158      * @return  a <code>Function</code> to invoke the specified method
159      */
160     public static JMethod make( final Method method ) {
161         //  Now categorize the method into static/instance
162         //  versus void/Object return value
163         int mods = method.getModifiers();
164         Class r = method.getReturnType();
165         if ( Modifier.isStatic( mods ) ) {
166             if ( r == null ) {
167                 return new StaticVoidFunction( method );
168             } else {
169                 return new StaticReturnFunction( method );
170             }
171         } else {    //  instance method
172             if ( r == null ) {
173                 return new InstanceVoidFunction( method );
174             } else {
175                 return new InstanceReturnFunction( method );
176             }
177         }
178     }
179 
180     /**
181      * Checks that the specified object is an instance of the underlying Java
182      * methods class.
183      *
184      * @param x the object to validate
185      * @return  the specified object if it passes validation
186      */
187     Object validate( final Object x ) {
188         if ( !clss.isInstance( x ) ) {
189             throw(
190                 Alerts.eval(
191                     "Unexpected type",
192                     "Java methods must be applied to valid class"
193                 ).
194                 culprit( "expected type", clss ).
195                 culprit( "actual type", x.getClass() ).
196                 culprit( "actual value", x ).
197                 mishap()
198             );
199         }
200         return x;
201     }
202 
203 }