diff options
author | Florian Dold <florian.dold@gmail.com> | 2013-08-27 17:16:18 +0000 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2013-08-27 17:16:18 +0000 |
commit | a942ffadee0fe9fd385decdf818ad6baae8c99b3 (patch) | |
tree | d500fbdba7379631b0591a19417c7c3f3df29194 /src/main/java/org/grothoff | |
parent | 6be9a1ed1b7847c795cb700e3e0bd87824fc0573 (diff) | |
download | gnunet-java-a942ffadee0fe9fd385decdf818ad6baae8c99b3.tar.gz gnunet-java-a942ffadee0fe9fd385decdf818ad6baae8c99b3.zip |
- adapted source tree structure to gradle/maven conventions
- added gradle wrapper
- fixes to adapt to GNUnet changes (new time unit, ...)
- helper process in util
- started implementing testbed
- skeleton for voting tools
- use new mq api
- implemented some more transport api
- mesh
Diffstat (limited to 'src/main/java/org/grothoff')
-rw-r--r-- | src/main/java/org/grothoff/Runabout.java | 574 | ||||
-rw-r--r-- | src/main/java/org/grothoff/package-info.java | 4 |
2 files changed, 578 insertions, 0 deletions
diff --git a/src/main/java/org/grothoff/Runabout.java b/src/main/java/org/grothoff/Runabout.java new file mode 100644 index 0000000..2a1dbb0 --- /dev/null +++ b/src/main/java/org/grothoff/Runabout.java | |||
@@ -0,0 +1,574 @@ | |||
1 | /* | ||
2 | * (C) 2002, 2003, 2004, 2005, 2006 Christian Grothoff | ||
3 | * | ||
4 | * The Runabout is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU General Public License as published by the Free Software | ||
6 | * Foundation; either version 2, or (at your option) any later version. The | ||
7 | * Runabout is distributed in the hope that it will be useful, but WITHOUT ANY | ||
8 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||
9 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
10 | * You should have received a copy of the GNU General Public License along with | ||
11 | * the Runabout; see the file COPYING. If not, write to the Free Software | ||
12 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
13 | * | ||
14 | * This software is also licensed under the Eclipse Public License v1.0 | ||
15 | * available at http://www.eclipse.org/legal/epl-v10.html. | ||
16 | */ | ||
17 | package org.grothoff; | ||
18 | |||
19 | import java.io.UnsupportedEncodingException; | ||
20 | import java.lang.reflect.Array; | ||
21 | import java.lang.reflect.InvocationTargetException; | ||
22 | import java.lang.reflect.Method; | ||
23 | import java.lang.reflect.Modifier; | ||
24 | import java.util.HashMap; | ||
25 | |||
26 | /** | ||
27 | * Runabout is a fast implementation of the Walkabout which is a variant of the | ||
28 | * Visitor Pattern that does not require an accept method and uses reflection | ||
29 | * instead. | ||
30 | * <p/> | ||
31 | * An instance of Runabout is able to walk over an arbitrary object graph using | ||
32 | * visit methods which take arguments of the specific type of the object to | ||
33 | * visit. For each node in the object graph the Runabout invokes the most | ||
34 | * appropriate visit method. | ||
35 | * <p/> | ||
36 | * Using the Runabout typically involves subclassing Runabout and adding a | ||
37 | * couple of visit methods. The Runabout provides a 'visitAppropriate' method | ||
38 | * which will invoke the most appropriate visit method of the current Runabout | ||
39 | * instance. If no visit method is applicable, visitAppropriate calls | ||
40 | * visitDefault() which, if not overridden, throws an exception. | ||
41 | * <p/> | ||
42 | * The elements of the object graph typically extend the Element class, which | ||
43 | * provides a generic way to quickly invoke the Runabout on all the fields of | ||
44 | * the Element. | ||
45 | * <p/> | ||
46 | * Note that the Runabout uses dynamic code generation and dynamic loading in | ||
47 | * order to be quickly able to invoke the appropriate visit methods. To make the | ||
48 | * dynamic code generation fast, the code inlines parts of Java class-files in | ||
49 | * binary form (ugly!).<br> | ||
50 | * A per-thread Cache is used to speed-up the creation of the Runabout by | ||
51 | * caching reflection, code creation and dynamic loading operations. | ||
52 | * <p/> | ||
53 | * <bf>Restrictions:</bf> Java semantics require: | ||
54 | * <ul> | ||
55 | * <li>all subclasses must be public, sadly this also means that <strong>you | ||
56 | * can not use an anonymous inner class</strong> (!)</li> | ||
57 | * <li>the types_length to all arguments of visit methods must be public</li> | ||
58 | * <li>all visit methods must be public (!)</li> | ||
59 | * </ul> | ||
60 | * Otherwise the visitor will die with an IllegalAccessError during execution. | ||
61 | * <p/> | ||
62 | * | ||
63 | * @author Christian Grothoff | ||
64 | * @version 5.0 | ||
65 | */ | ||
66 | public class Runabout { | ||
67 | |||
68 | /** | ||
69 | * Singleton of the Runabout.Cache. We cache reflective information per VM; | ||
70 | * this avoids the need for repeated reflection, code generation and | ||
71 | * dispatching map updates. | ||
72 | */ | ||
73 | private static final Runabout.Cache cache_ = new Runabout.Cache(); | ||
74 | |||
75 | /** | ||
76 | * map_ contains a mapping from a class to the appropriate visit method. | ||
77 | * Note that at the beginning, map_ only contains the explicit mappings as | ||
78 | * given by the visit methods. Over time, map_ will be amended to also | ||
79 | * contain direct mappings for subclasses to the appropriate visit methods | ||
80 | * if they are used. | ||
81 | */ | ||
82 | private final HashMap<Class, Runabout.Code> map_; | ||
83 | |||
84 | /** | ||
85 | * Code to invoke if no visitor is found (used to avoid scanning the | ||
86 | * hierarchy again and again). | ||
87 | */ | ||
88 | private final Code noCode = new NoCode(); | ||
89 | |||
90 | /** | ||
91 | * Set when the subclass of the runabout is not public. | ||
92 | */ | ||
93 | private final boolean isPublic; | ||
94 | |||
95 | /** | ||
96 | * Create a Runabout. | ||
97 | */ | ||
98 | public Runabout() { | ||
99 | this.isPublic = Modifier.isPublic(getClass().getModifiers()); | ||
100 | map_ = cache_.get(this); | ||
101 | } | ||
102 | |||
103 | /** | ||
104 | * Call the appropriate visit method. Use this method if you are visiting a | ||
105 | * graph of objects (no primitives). | ||
106 | * | ||
107 | * @param o the object to visit | ||
108 | */ | ||
109 | public void visitAppropriate(Object o) { | ||
110 | getAppropriateCodeInternal(o.getClass()).visit(this, o); | ||
111 | } | ||
112 | |||
113 | /** | ||
114 | * Obtain the appropriate code to call for class c. The method either | ||
115 | * obtains the code quickly from the code map (fast path) or by calling the | ||
116 | * lookup method getAppropriateCode. | ||
117 | * | ||
118 | * @return the code, never returns null | ||
119 | */ | ||
120 | private Code getAppropriateCodeInternal(Class c) { | ||
121 | synchronized (cache_) { | ||
122 | Code co = map_.get(c); | ||
123 | if (co != null) | ||
124 | return co; | ||
125 | co = getAppropriateCode(c); | ||
126 | if (co == null) | ||
127 | co = noCode; | ||
128 | map_.put(c, co); | ||
129 | return co; | ||
130 | } | ||
131 | } | ||
132 | |||
133 | /** | ||
134 | * Find the appropriate Code to call in the map. If no code is found, return | ||
135 | * null. This lookup strategy first attempts to find a visit method defined | ||
136 | * for the parent classes of c. If no such method exists, it attempts to | ||
137 | * find an unambiguous visit method matching any interface transitively | ||
138 | * implemented by c. If that does not exist either, null is returned. If | ||
139 | * only an ambiguous visit method exists, an exception is raised. | ||
140 | * | ||
141 | * @param c the class for which to find the code | ||
142 | * @return the code to run, or null if no code was found | ||
143 | * @throws RunaboutException if the lookup would be ambiguous | ||
144 | */ | ||
145 | private Code getAppropriateCode(Class c) { | ||
146 | if (c.isArray()) { | ||
147 | // uh uh, array subtyping in action... | ||
148 | int dims = 1; | ||
149 | Class component = c.getComponentType(); | ||
150 | while (component.isArray()) { | ||
151 | dims++; | ||
152 | component = component.getComponentType(); | ||
153 | } | ||
154 | Class superComp = component.getSuperclass(); | ||
155 | while (superComp != null) { | ||
156 | Code co = map_.get(Array.newInstance(superComp, new int[dims]).getClass()); | ||
157 | if (co != null) | ||
158 | return co; | ||
159 | superComp = superComp.getSuperclass(); | ||
160 | } | ||
161 | // now try subtyping with multi-dimensional Object[] | ||
162 | // (see crazy runabout test). | ||
163 | Class objectClass = c.getSuperclass(); | ||
164 | while (dims > 1) { | ||
165 | Code co = map_.get(Array.newInstance(objectClass, new int[dims]).getClass()); | ||
166 | if (co != null) | ||
167 | return co; | ||
168 | dims--; | ||
169 | } | ||
170 | } | ||
171 | Class cl = c.getSuperclass(); | ||
172 | while (cl != null) { | ||
173 | Code co = map_.get(cl); | ||
174 | if (co != null) | ||
175 | return co; | ||
176 | cl = cl.getSuperclass(); | ||
177 | } | ||
178 | return getAppropriateCode_ifc(c, c); | ||
179 | } | ||
180 | |||
181 | /** | ||
182 | * Find the appropriate Code to call in the map. If no code is found, return | ||
183 | * null. | ||
184 | * | ||
185 | * @param c the class for which to find the code | ||
186 | * @param cl the class where to start looking from | ||
187 | * @return the code to run, or null if no code was found | ||
188 | */ | ||
189 | private Code getAppropriateCode_ifc(Class c, Class cl) { | ||
190 | Code co = null; | ||
191 | while (cl != null) { | ||
192 | Class[] ifc = cl.getInterfaces(); | ||
193 | for (Class anIfc : ifc) { | ||
194 | Code r = map_.get(anIfc); | ||
195 | if (r != null) { | ||
196 | if ((co != null) && (r != co)) | ||
197 | throw new RunaboutException("Ambiguous resolution for visit call to " | ||
198 | + c + " in " + this.getClass().getName()); | ||
199 | co = r; | ||
200 | } | ||
201 | } | ||
202 | for (Class anIfc : ifc) { | ||
203 | Code r = getAppropriateCode_ifc(c, anIfc); | ||
204 | if (r != null) { | ||
205 | if ((co != null) && (r != co)) | ||
206 | throw new RunaboutException("Ambiguous resolution for visit call to " | ||
207 | + c + " in " + this.getClass().getName()); | ||
208 | co = r; | ||
209 | } | ||
210 | } | ||
211 | cl = cl.getSuperclass(); | ||
212 | } | ||
213 | return co; | ||
214 | } | ||
215 | |||
216 | /** | ||
217 | * Generate the initial version of the map that maps classes to Code to call | ||
218 | * the appropriate visit method. | ||
219 | */ | ||
220 | final HashMap<Class, Runabout.Code> makeMap() { | ||
221 | // get number of methods | ||
222 | int size = 0; | ||
223 | Class me = this.getClass(); | ||
224 | while (me != null) { | ||
225 | size += me.getDeclaredMethods().length; | ||
226 | me = me.getSuperclass(); | ||
227 | } | ||
228 | // create map with slight over-estimate | ||
229 | HashMap<Class, Runabout.Code> result = new HashMap<Class, Runabout.Code>(size * 2); | ||
230 | // for all methods - create call code, put | ||
231 | me = this.getClass(); | ||
232 | while (me != null) { | ||
233 | Method[] methods = me.getDeclaredMethods(); | ||
234 | for (final Method m : methods) { | ||
235 | if ((m.getName().equals("visit")) | ||
236 | && (!Modifier.isStatic(m.getModifiers()))) { | ||
237 | Class[] args = m.getParameterTypes(); | ||
238 | if (args.length != 1) | ||
239 | throw new RunaboutException("Invalid number of arguments for Runabout in method " | ||
240 | + m); | ||
241 | final Class viC = args[0]; | ||
242 | if (result.get(viC) != null) | ||
243 | continue; | ||
244 | Code co; | ||
245 | if (isPublic) { | ||
246 | // invoke the visitor with generated code | ||
247 | co = makeCode(viC); | ||
248 | if (co == null) { | ||
249 | throw new RunaboutException("Could not create/load dynamic code!"); | ||
250 | } | ||
251 | } else { | ||
252 | if (!m.isAccessible()) { | ||
253 | m.setAccessible(true); | ||
254 | } | ||
255 | // invoke the visitor with an anonymous inner class, | ||
256 | // allows for the runabout to be public as the method made accessible | ||
257 | // by the Method instance. | ||
258 | // For Java 7+ the performance of this could be improved by using a MethodHandle | ||
259 | co = new Code() { | ||
260 | @Override | ||
261 | public void visit(Runabout r, Object o) { | ||
262 | try { | ||
263 | |||
264 | m.invoke(r, o); | ||
265 | } catch (IllegalAccessException e) { | ||
266 | throw new RunaboutException(e.toString()); | ||
267 | } catch (InvocationTargetException e) { | ||
268 | System.err.println("stacktrace:"); | ||
269 | e.getCause().printStackTrace(System.err); | ||
270 | throw new RunaboutException(e.getCause().toString()); | ||
271 | } | ||
272 | } | ||
273 | }; | ||
274 | } | ||
275 | |||
276 | result.put(viC, co); | ||
277 | |||
278 | } | ||
279 | } | ||
280 | me = me.getSuperclass(); | ||
281 | } | ||
282 | return result; | ||
283 | } | ||
284 | |||
285 | /** | ||
286 | * Create the code to invoke a visit method. | ||
287 | * | ||
288 | * @param c the type of the argument to the visit method | ||
289 | */ | ||
290 | private Code makeCode(Class c) { | ||
291 | byte[] myName // Lovm/util/RunaboutExample; substitute | ||
292 | = canonicalName(getClass(), false); | ||
293 | final int myNameLen = myName.length; | ||
294 | final int myNameLenM2 = myNameLen - 2; | ||
295 | byte[] cName // Ljava/lang/String; substitute | ||
296 | = canonicalName(c, false); | ||
297 | byte[] cNameCast = canonicalName(c, true); | ||
298 | final int cNameLen = cName.length; | ||
299 | final int cNameLenCast = cNameCast.length - 2; | ||
300 | byte[] code = new byte[genCodeTemplate.length - 62 + myNameLenM2 | ||
301 | + cNameLenCast + cNameLen]; | ||
302 | |||
303 | // Build code by substituting a few strings in genCodeTemplate. | ||
304 | // 117-145: org/grothoff/RunaboutExample => myName | ||
305 | // 148-164: java/lang/String => cName | ||
306 | // 192-200: XXXXXXXX => number | ||
307 | // 250-271: (Ljava/lang/String;)V => "("+cName+")V" | ||
308 | |||
309 | System.arraycopy(genCodeTemplate, 0, code, 0, 115); | ||
310 | code[115] = (byte) ((myNameLenM2) >> 8); | ||
311 | code[116] = (byte) ((myNameLenM2) & 255); | ||
312 | System.arraycopy(myName, 1, code, 117, myNameLenM2); | ||
313 | code[117 + myNameLenM2] = 1; // tag for string | ||
314 | code[118 + myNameLenM2] = (byte) ((cNameLenCast) >> 8); | ||
315 | code[119 + myNameLenM2] = (byte) ((cNameLenCast) & 255); | ||
316 | System.arraycopy(cNameCast, 1, code, 120 + myNameLenM2, cNameLenCast); | ||
317 | System.arraycopy(genCodeTemplate, 164, code, 120 + myNameLenM2 | ||
318 | + cNameLenCast, 248 - 164); | ||
319 | code[120 + myNameLenM2 + cNameLenCast + 248 - 164] = (byte) ((cNameLen + 3) >> 8); | ||
320 | code[120 + myNameLenM2 + cNameLenCast + 249 - 164] = (byte) ((cNameLen + 3) & 255); | ||
321 | code[120 + myNameLenM2 + cNameLenCast + 250 - 164] = (byte) '('; | ||
322 | System.arraycopy(cName, 0, code, 120 + myNameLenM2 + cNameLenCast + 251 | ||
323 | - 164, cNameLen); | ||
324 | code[120 + myNameLenM2 + cNameLenCast + 250 - 164 + cNameLen + 1] = (byte) ')'; | ||
325 | code[120 + myNameLenM2 + cNameLenCast + 250 - 164 + cNameLen + 2] = (byte) 'V'; | ||
326 | System.arraycopy(genCodeTemplate, | ||
327 | 271, | ||
328 | code, | ||
329 | 120 + myNameLenM2 + cNameLenCast + 250 - 164 | ||
330 | + cNameLen + 3, | ||
331 | genCodeTemplate.length - 271); | ||
332 | return cache_.loadCode(code, 120 + myNameLenM2 + cNameLenCast + 192 | ||
333 | - 164); | ||
334 | } | ||
335 | |||
336 | /** | ||
337 | * Get the class name in canonical form. | ||
338 | * | ||
339 | * @param cls the class, may not be primitive | ||
340 | * @return the ovm name, following the convention of | ||
341 | * {@code java.util.Class.forName} according to the JavaDoc | ||
342 | * specification (JDK 1.2.2/1.3/1.4) which differs from the actual | ||
343 | * implementation in both SUN and IBM VMs. | ||
344 | */ | ||
345 | private static byte[] canonicalName(Class cls, boolean forCast) { | ||
346 | String cname = cls.getName(); | ||
347 | try { | ||
348 | byte[] utf = cname.getBytes("UTF-8"); | ||
349 | int len = utf.length; // may be > cname.length()! | ||
350 | if ((cname.charAt(0) != '[') || (forCast)) { | ||
351 | byte[] ret = new byte[len + 2]; | ||
352 | ret[0] = (byte) 'L'; | ||
353 | System.arraycopy(utf, 0, ret, 1, len); | ||
354 | ret[len + 1] = (byte) ';'; | ||
355 | for (int i = len; i > 0; i--) | ||
356 | if (ret[i] == (byte) '.') | ||
357 | ret[i] = (byte) '/'; | ||
358 | return ret; | ||
359 | } | ||
360 | for (int i = len - 1; i >= 0; i--) | ||
361 | if (utf[i] == (byte) '.') | ||
362 | utf[i] = (byte) '/'; | ||
363 | return utf; | ||
364 | } catch (UnsupportedEncodingException uee) { | ||
365 | throw new RunaboutException("UTF8 encoding not supported!?: " + uee); | ||
366 | } | ||
367 | } | ||
368 | |||
369 | /** | ||
370 | * The Runabout.Cache is essentially a per-class cache of the internal | ||
371 | * constant state of a Runabout instance. It contains the generated code to | ||
372 | * quicly invoke the appropriate visit methods. | ||
373 | * | ||
374 | * @author Christian Grothoff | ||
375 | */ | ||
376 | static final class Cache { | ||
377 | |||
378 | /** | ||
379 | * ClassLoader to use to load the code. | ||
380 | */ | ||
381 | private final ClassLoader loader_; | ||
382 | |||
383 | /** | ||
384 | * Last name used by the class loader. | ||
385 | */ | ||
386 | private final byte[] lastName_; | ||
387 | |||
388 | /** | ||
389 | * Mapping of classes to Maps. | ||
390 | */ | ||
391 | private final HashMap<Class, HashMap<Class, Runabout.Code>> cachemap_; | ||
392 | |||
393 | /** | ||
394 | * Code that the loader should use. | ||
395 | */ | ||
396 | byte[] code; | ||
397 | |||
398 | /** | ||
399 | * Create the Cache. | ||
400 | */ | ||
401 | Cache() { | ||
402 | loader_ = new ClassLoader() { | ||
403 | public Class<?> loadClass(String name) | ||
404 | throws ClassNotFoundException { | ||
405 | //noinspection StringEquality | ||
406 | if (name == "Code") // == works here, as both strings are guaranteed to be interned | ||
407 | return defineClass(null, code, 0, code.length); | ||
408 | return Thread.currentThread().getContextClassLoader().loadClass(name); | ||
409 | } | ||
410 | }; | ||
411 | cachemap_ = new HashMap<Class, HashMap<Class, Runabout.Code>>(); | ||
412 | lastName_ = new byte[8]; | ||
413 | for (int i = 0; i < 8; i++) | ||
414 | lastName_[i] = (byte) '0'; | ||
415 | } | ||
416 | |||
417 | /** | ||
418 | * Create a class from the given bytecode. Since classes loaded by the | ||
419 | * same Loader must have a unique name, this method patches the bytecode | ||
420 | * at the given offset, changing the next 8 characters to a unique Java | ||
421 | * classname. | ||
422 | * | ||
423 | * @param byteCode the bytecode of the class which must describe a class | ||
424 | * of type 'Code'. The class must contain a sequence XXXXXXXX at | ||
425 | * offset xIdx where the classname is to be patched | ||
426 | * @param xIdx the index of the XXXXXXXX sequence | ||
427 | * @return an instance of the loaded class, null on error | ||
428 | * @throws ArrayIndexOutOfBoundsException if more than 62<sup>8</sup> | ||
429 | * classes are loaded :-) | ||
430 | * @throws RunaboutException if there are problems with dynamic loading | ||
431 | * of the byteCode | ||
432 | */ | ||
433 | Code loadCode(byte[] byteCode, int xIdx) { | ||
434 | boolean overflow = true; | ||
435 | int index = 7; | ||
436 | while (overflow) { | ||
437 | overflow = false; | ||
438 | lastName_[index]++; | ||
439 | if (lastName_[index] == (byte) ('9' + 1)) | ||
440 | lastName_[index] = (byte) 'A'; | ||
441 | if (lastName_[index] == (byte) ('Z' + 1)) | ||
442 | lastName_[index] = (byte) 'a'; | ||
443 | if (lastName_[index] == (byte) ('z' + 1)) { | ||
444 | lastName_[index] = (byte) '0'; | ||
445 | overflow = true; | ||
446 | index--; | ||
447 | } | ||
448 | } | ||
449 | System.arraycopy(lastName_, 0, byteCode, xIdx, 8); | ||
450 | code = byteCode; | ||
451 | |||
452 | Code co; | ||
453 | try { | ||
454 | co = (Code) loader_.loadClass("Code").newInstance(); | ||
455 | } catch (InstantiationException ie) { | ||
456 | throw new RunaboutException(ie.toString()); | ||
457 | } catch (ClassNotFoundException cnfe) { | ||
458 | throw new RunaboutException(cnfe.toString()); | ||
459 | } catch (IllegalArgumentException iae) { | ||
460 | throw new RunaboutException(iae.toString()); | ||
461 | } catch (ClassFormatError cfe) { | ||
462 | throw new RunaboutException(cfe.toString()); | ||
463 | } catch (IllegalAccessException iae) { | ||
464 | throw new RunaboutException(iae.toString()); | ||
465 | } | ||
466 | code = null; // help GC | ||
467 | return co; | ||
468 | } | ||
469 | |||
470 | /** | ||
471 | * Obtain a map from the cache. | ||
472 | */ | ||
473 | synchronized HashMap<Class, Runabout.Code> get(Runabout r) { | ||
474 | Class c = r.getClass(); | ||
475 | HashMap<Class, Runabout.Code> map = cachemap_.get(c); | ||
476 | if (map == null) { | ||
477 | map = r.makeMap(); | ||
478 | cachemap_.put(c, map); | ||
479 | } | ||
480 | return map; | ||
481 | } | ||
482 | |||
483 | } // end of Runabout.Cache | ||
484 | |||
485 | /** | ||
486 | * Code is the generic interface that all generated classes implement. It is | ||
487 | * used to quickly map a given class to the appropriate visit method. | ||
488 | * | ||
489 | * @author Christian Grothoff | ||
490 | */ | ||
491 | public static abstract class Code { | ||
492 | public Code() { | ||
493 | } | ||
494 | |||
495 | public abstract void visit(Runabout r, Object o); | ||
496 | |||
497 | } // end of Runabout.Code | ||
498 | |||
499 | /** | ||
500 | * Implementation of Code that is called if no visit method matches (calls | ||
501 | * visitDefault). | ||
502 | * | ||
503 | * @author Christian Grothoff | ||
504 | */ | ||
505 | static final class NoCode extends Code { | ||
506 | public final void visit(Runabout r, Object o) { | ||
507 | r.visitDefault(o); | ||
508 | } | ||
509 | } // end of Runabout.NoCode | ||
510 | |||
511 | /** | ||
512 | * Override this method to provide a default behavior when no other visit | ||
513 | * matches. The Runabout semantics are to search for a visit(X) and if there | ||
514 | * is no match, call visitDefault(). As usual with the Runabout, visit(X) | ||
515 | * looks at classes before interfaces. By default, visitDefault throws an | ||
516 | * exception. | ||
517 | */ | ||
518 | protected void visitDefault(Object o) { | ||
519 | throw new RunaboutException("No visit method defined in " | ||
520 | + this.getClass() + " for " + o.getClass()); | ||
521 | } | ||
522 | |||
523 | /** | ||
524 | * Generic Exception for problems in the Runabout. | ||
525 | * | ||
526 | * @author Christian Grothoff | ||
527 | */ | ||
528 | public static final class RunaboutException extends RuntimeException { | ||
529 | RunaboutException(String s) { | ||
530 | super(s); | ||
531 | } | ||
532 | } | ||
533 | |||
534 | /** | ||
535 | * Compile 'GenCodeXXXXXXXX.java' with the option '-g:none' to tell javac | ||
536 | * not to include any debugging information. This is the generated class | ||
537 | * file. | ||
538 | */ | ||
539 | private final static byte genCodeTemplate[] = {-54, -2, -70, -66, 0, 0, 0, | ||
540 | 49, 0, 22, 10, 0, 6, 0, 12, 7, 0, 13, 7, 0, 14, 10, 0, 2, 0, 15, 7, | ||
541 | 0, 16, 7, 0, 18, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, | ||
542 | 41, 86, 1, 0, 4, 67, 111, 100, 101, 1, 0, 5, 118, 105, 115, 105, | ||
543 | 116, 1, 0, 44, 40, 76, 111, 114, 103, 47, 103, 114, 111, 116, 104, | ||
544 | 111, 102, 102, 47, 82, 117, 110, 97, 98, 111, 117, 116, 59, 76, | ||
545 | 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, | ||
546 | 116, 59, 41, 86, 12, 0, 7, 0, 8, 1, 0, 28, 111, 114, 103, 47, 103, | ||
547 | 114, 111, 116, 104, 111, 102, 102, 47, 82, 117, 110, 97, 98, 111, | ||
548 | 117, 116, 69, 120, 97, 109, 112, 108, 101, 1, 0, 16, 106, 97, 118, | ||
549 | 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 12, 0, | ||
550 | 10, 0, 20, 1, 0, 28, 111, 114, 103, 47, 103, 114, 111, 116, 104, | ||
551 | 111, 102, 102, 47, 71, 101, 110, 67, 111, 100, 101, 88, 88, 88, 88, | ||
552 | 88, 88, 88, 88, 7, 0, 21, 1, 0, 26, 111, 114, 103, 47, 103, 114, | ||
553 | 111, 116, 104, 111, 102, 102, 47, 82, 117, 110, 97, 98, 111, 117, | ||
554 | 116, 36, 67, 111, 100, 101, 1, 0, 12, 73, 110, 110, 101, 114, 67, | ||
555 | 108, 97, 115, 115, 101, 115, 1, 0, 21, 40, 76, 106, 97, 118, 97, | ||
556 | 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 86, | ||
557 | 1, 0, 21, 111, 114, 103, 47, 103, 114, 111, 116, 104, 111, 102, | ||
558 | 102, 47, 82, 117, 110, 97, 98, 111, 117, 116, 0, 33, 0, 5, 0, 6, 0, | ||
559 | 0, 0, 0, 0, 2, 0, 1, 0, 7, 0, 8, 0, 1, 0, 9, 0, 0, 0, 17, 0, 1, 0, | ||
560 | 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 0, 0, 1, 0, 10, 0, 11, | ||
561 | 0, 1, 0, 9, 0, 0, 0, 24, 0, 2, 0, 3, 0, 0, 0, 12, 43, -64, 0, 2, | ||
562 | 44, -64, 0, 3, -74, 0, 4, -79, 0, 0, 0, 0, 0, 1, 0, 19, 0, 0, 0, | ||
563 | 10, 0, 1, 0, 6, 0, 17, 0, 9, 4, 9}; // GenCodeXXXXXXXX.class | ||
564 | |||
565 | public static void main(String[] args) { | ||
566 | Runabout r = new Runabout() { | ||
567 | public void visit(String s) { | ||
568 | System.out.println("hi!!"); | ||
569 | } | ||
570 | }; | ||
571 | r.visitAppropriate("foo"); | ||
572 | } | ||
573 | |||
574 | } // end of Runabout | ||
diff --git a/src/main/java/org/grothoff/package-info.java b/src/main/java/org/grothoff/package-info.java new file mode 100644 index 0000000..2a8b8f0 --- /dev/null +++ b/src/main/java/org/grothoff/package-info.java | |||
@@ -0,0 +1,4 @@ | |||
1 | /** | ||
2 | * Pure java implementation of single argument multiple dispatch | ||
3 | */ | ||
4 | package org.grothoff; | ||