jmultimethod

multimethods for Java using annotations
git clone https://logand.com/git/jmultimethod.git/
Log | Files | Refs | LICENSE

Multimethod.java (5166B)


      1 // Copyright (c) 2009, 2015 Tomas Hlavaty All rights reserved.
      2 
      3 // Redistribution and use in source and binary forms, with or without
      4 // modification, are permitted provided that the following conditions
      5 // are met:
      6 
      7 // Redistributions of source code must retain the above copyright
      8 // notice, this list of conditions and the following
      9 // disclaimer. Redistributions in binary form must reproduce the above
     10 // copyright notice, this list of conditions and the following
     11 // disclaimer in the documentation and/or other materials provided
     12 // with the distribution.
     13 
     14 // Neither the name of jmultimethod nor the names of its contributors
     15 // may be used to endorse or promote products derived from this
     16 // software without specific prior written permission.
     17 
     18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     21 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     22 // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     23 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     24 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     25 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
     27 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
     29 // OF THE POSSIBILITY OF SUCH DAMAGE.
     30 
     31 package jmultimethod;
     32 
     33 import java.lang.annotation.Annotation;
     34 import java.lang.reflect.Method;
     35 import java.util.ArrayList;
     36 import java.util.Arrays;
     37 import java.util.Comparator;
     38 
     39 public class Multimethod {
     40 
     41     protected String name;
     42     protected final ArrayList<Method> methods = new ArrayList<Method>();
     43     protected final MethodComparator methodComparator = new MethodComparator();
     44 
     45     public Multimethod(String name, Class... classes) {
     46         this.name = name;
     47         for(Class c: classes) {
     48             add(c);
     49         }
     50     }
     51 
     52     public void add(Class c) {
     53         for(Method m: c.getMethods()) {
     54             for(Annotation ma: m.getAnnotations()) {
     55                 if(ma instanceof Multi) {
     56                     Multi g = (Multi) ma;
     57                     if(this.name.equals(g.value())) {
     58                         methods.add(m);
     59                     }
     60                 }
     61             }
     62         }
     63         sort();
     64     }
     65 
     66     protected void sort() {
     67         Method[] a = new Method[methods.size()];
     68         methods.toArray(a);
     69         Arrays.sort(a, methodComparator);
     70         methods.clear();
     71         for(Method m: a) {
     72             methods.add(m);
     73         }
     74     }
     75 
     76     protected class MethodComparator implements Comparator<Method> {
     77         @Override
     78         public int compare(Method l, Method r) {
     79             if(l.equals(r)) {
     80                 return 0;
     81             }
     82             // most specific methods first
     83             Class[] lc = l.getParameterTypes();
     84             Class[] rc = r.getParameterTypes();
     85             for(int i = 0; i < lc.length; i++) {
     86                 String lv = value(l, i);
     87                 String rv = value(r, i);
     88                 if(lv == null) {
     89                     if(rv == null) {
     90                         if(lc[i].isAssignableFrom(rc[i])) {
     91                             return 1;
     92                         }
     93                     } else {
     94                         return 1;
     95                     }
     96                 } else {
     97                     if(rv == null) {
     98                         return -1;
     99                     }
    100                 }
    101             }
    102             return -1;
    103         }
    104     }
    105 
    106     protected String value(Method method, int arg) {
    107         Annotation[] a = method.getParameterAnnotations()[arg];
    108         for(Annotation p: a) {
    109             if(p instanceof V) {
    110                 V v = (V) p;
    111                 return v.value();
    112             }
    113         }
    114         return null;
    115     }
    116 
    117     protected boolean isApplicable(Method method, Object... args) {
    118         Class[] c = method.getParameterTypes();
    119         for(int i = 0; i < c.length; i++) {
    120             // must be instanceof and equal to annotated value if present
    121             if(c[i].isInstance(args[i])) {
    122                 String v = value(method, i);
    123                 if(v != null && !v.equals(args[i])) {
    124                     return false;
    125                 }
    126             } else {
    127                 if(args[i] != null || !Object.class.equals(c[i])) {
    128                     return false;
    129                 }
    130             }
    131         }
    132         return true;
    133     }
    134 
    135     public Object invoke(Object self, Object... args) {
    136         Method m = null; // first applicable method (most specific)
    137         for(Method method: methods) {
    138             if(isApplicable(method, args)) {
    139                 m = method;
    140                 break;
    141             }
    142         }
    143         if(m == null) {
    144             throw new RuntimeException("No applicable method '" + name + "'.");
    145         }
    146         try {
    147             return m.invoke(self, args);
    148         } catch (Exception e) {
    149             throw new RuntimeException("Method invocation failed '" + name + "'.");
    150         }
    151     }
    152 }