1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.millscript.millscript.vm;
23
24 import org.millscript.commons.util.EList;
25 import org.millscript.commons.util.EMap;
26 import org.millscript.commons.util.IList;
27 import org.millscript.commons.util.ListIterator;
28 import org.millscript.commons.util.MapIterator;
29 import org.millscript.commons.util.list.ELinkedList;
30 import org.millscript.commons.util.map.EHashMap;
31 import org.millscript.millscript.alert.Alerts;
32 import org.millscript.millscript.functions.Function;
33 import org.millscript.millscript.functions.JConstructor;
34 import org.millscript.millscript.functions.JMethod;
35 import org.millscript.millscript.functions.JOConstructor;
36 import org.millscript.millscript.functions.JOMethod;
37 import org.millscript.millscript.functions.JRecognizer;
38
39 import java.lang.reflect.Array;
40 import java.lang.reflect.Constructor;
41 import java.lang.reflect.Method;
42 import java.util.Vector;
43
44 /**
45 * This class imports all available methods and constructors in a specified
46 * Java class into the specified package. The available methods and
47 * constructors are imported as functions. If there are multiple constructors
48 * or methods that take the same number of parameters, they will not be
49 * imported.
50 * <p>
51 * The imported constructor function will be called by the name of the class
52 * prefixed with "new". A recognizer function for instances of this class can
53 * be imported called by the name of the class prefixed with "is".
54 * </p>
55 */
56 public class ImportJavaClass {
57
58 /**
59 * The class we intend to import.
60 */
61 private final Class clss;
62
63 /**
64 * The package we will import into.
65 */
66 private final Package pack;
67
68 /**
69 * Constructs an instance to import all methods and constructors in the
70 * specified Java class into the specified package.
71 *
72 * @param className the class to import methods and constructors from
73 * @param p the package to define the new functions in
74 */
75 public ImportJavaClass( final String className, final Package p ) {
76 Class c = null;
77 this.pack = p;
78 try {
79 c = Class.forName( className );
80 } catch ( ClassNotFoundException ex ) {
81 throw(
82 Alerts.fault( "Cannot find this class " + className ).mishap()
83 );
84 }
85 clss = c;
86 }
87
88 /**
89 * Convenience method to import all available constructors and methods.
90 */
91 public void importAll() {
92 importAllConstructors();
93 importAllMethods();
94 }
95
96 /**
97 * Removes duplicate entries from the vector of lists of
98 * constructors/methods. Specifically, this method removes entries from the
99 * vector where there is more than one item in the list. e.g. this means if
100 * two constructors or methods have the same name they will both be
101 * removed.
102 *
103 * @param v the vector of lists of constructors/methods to check
104 * @param componentClass the class we are importing from
105 * @return an array of constructors/methods indexed by the number of
106 * parameters the constructor/method takes.
107 */
108 @SuppressWarnings( "unchecked" )
109 private static < T > T[] pruneDups( final Vector< EList< T > > v, final Class< T > componentClass ) {
110
111
112 int hi = 0;
113
114
115 int siz = v.size();
116 for ( int i = 0; i < siz; i++ ) {
117
118 EList< T > list = v.get( i );
119 if ( list != null ) {
120 if ( list.size() > 1 ) {
121
122
123 v.set( i, null );
124 } else {
125
126
127 hi = Math.max( hi, i + 1 );
128 }
129 }
130 }
131
132
133 T[] result = (T[]) Array.newInstance( componentClass, hi );
134
135
136 for ( int i = 0; i < siz; i++ ) {
137 EList< T > list = v.get( i );
138 if ( list != null ) {
139 result[ i ] = list.get0( 0 );
140 }
141 }
142 return result;
143 }
144
145 /**
146 * Returns the number of non-null elements in the array.
147 *
148 * @param objs the array to count non-null elements in
149 * @return the number of non-null elements
150 */
151 private int countNonNulls( final Object[] objs ) {
152 int count = 0;
153 for ( int i = 0; i < objs.length; i++ ) {
154 if ( objs[ i ] != null ) {
155 count += 1;
156 }
157 }
158 return count;
159 }
160
161 /**
162 * Returns the first non-null item in the array.
163 *
164 * @param objs the array to search
165 * @return the first non-null object, or <code>null</code> if all the
166 * objects in the array are <code>null</code>
167 */
168 private Object firstItem( final Object[] objs ) {
169 for ( int i = 0; i < objs.length; i++ ) {
170 if ( objs[ i ] != null ) {
171 return objs[ i ];
172 }
173 }
174 return null;
175 }
176
177 /**
178 * Return the name of the specified class without any package prefix. e.g.
179 * when java.lang.Object is specified, this method would return Object.
180 *
181 * @param name the class name
182 * @return the short version of the class name
183 */
184 private String shortClassName( final String name ) {
185 int n = name.lastIndexOf( '.' );
186 if ( n == -1 ) {
187 return name;
188 }
189 return name.substring( n + 1 );
190 }
191
192 /**
193 * Imports a recognizer function for this class.
194 */
195 private void importRecognizer() {
196 String name = "is" + shortClassName( clss.getName() );
197 pack.bindConst(
198 name,
199 new JRecognizer( clss )
200 );
201 }
202
203 /**
204 * Imports all available constructors in the Java class. This will ignore
205 *constructors which take the same number of parameters.
206 */
207 private void importAllConstructors() {
208
209 Constructor[] cons = clss.getConstructors();
210
211
212
213 Vector< EList< Constructor > > v = new Vector< EList< Constructor > >();
214 for ( int i = 0; i < cons.length; i++ ) {
215 Constructor c = cons[ i ];
216
217 int n = c.getParameterTypes().length;
218
219 v.setSize( n + 1 );
220
221 EList< Constructor > list = v.get( n );
222 if ( list == null ) {
223 list = new ELinkedList< Constructor >();
224 }
225
226 list.addLast( c );
227
228 v.set( n, list );
229 }
230
231
232
233 Constructor[] result = pruneDups( v, Constructor.class );
234
235
236
237 int total = countNonNulls( result );
238
239
240 if ( total > 0 ) {
241
242 String name = "new" + shortClassName( clss.getName() );
243
244
245
246 pack.bindConst(
247 name,
248 (
249 total == 1 ?
250 (Function)new JConstructor( (Constructor)firstItem( result ) ) :
251 (Function)new JOConstructor( result )
252 ).modName( name )
253 );
254 }
255 }
256
257 /**
258 * Imports all available methods in the Java class. This will ignore
259 * methods which take the same number of parameters.
260 */
261 private void importAllMethods() {
262
263 Method[] meths = clss.getMethods();
264
265
266 EMap< String, EList< Method > > map = new EHashMap< String, EList< Method > >();
267 for ( int i = 0; i < meths.length; i++ ) {
268 Method m = meths[ i ];
269 String name = m.getName();
270 EList< Method > list = map.get( name );
271 if ( list == null ) {
272 list = new ELinkedList< Method >();
273 map.insert( name, list );
274 }
275 list.addLast( m );
276 }
277
278 MapIterator< String, EList< Method > > it = map.iterator( true );
279 while ( it.hasNext() ) {
280 importMethod( it.nextKey(), it.currentValue() );
281 }
282 }
283
284 /**
285 * Imports the specified list of methods, removing any methods that take
286 * the same number of paramters.
287 *
288 * @param name the name of the method being imported, which will be the
289 * name bound in the package
290 * @param methods the list of methods to import
291 */
292 private void importMethod( final String name, final IList< Method > methods ) {
293
294
295
296 Vector< EList< Method > > v = new Vector< EList< Method > >();
297 ListIterator< Method > it = methods.iterator( true );
298 while ( it.hasNext() ) {
299 Method m = it.nextValue();
300
301 int n = m.getParameterTypes().length;
302
303 v.setSize( n + 1 );
304
305 EList< Method > list = v.get( n );
306 if ( list == null ) {
307 list = new ELinkedList< Method >();
308 }
309
310 list.addLast( m );
311
312 v.set( n, list );
313 }
314
315
316
317 Method[] result = pruneDups( v, Method.class );
318
319
320 int total = countNonNulls( result );
321
322
323 if ( total > 0 && pack.hasPervasiveImport( name ) == null ) {
324
325
326 pack.bindConst(
327 name,
328 (
329 total > 0 ?
330 (Function)JMethod.make( (Method)firstItem( result ) ) :
331 (Function)new JOMethod( result )
332 ).modName( name )
333 );
334 }
335 }
336 }