boot/src/main/java/org/apidesign/html/boot/impl/FnUtils.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Sat, 29 Jun 2013 22:44:59 +0200
changeset 171 54ac82353158
parent 163 2652760705d6
child 183 19ee92613f00
permissions -rw-r--r--
Can callback @JavaScriptBody methods in the same class
     1 /**
     2  * HTML via Java(tm) Language Bindings
     3  * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     4  *
     5  * This program is free software: you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation, version 2 of the License.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details. apidesign.org
    13  * designates this particular file as subject to the
    14  * "Classpath" exception as provided by apidesign.org
    15  * in the License file that accompanied this code.
    16  *
    17  * You should have received a copy of the GNU General Public License
    18  * along with this program. Look for COPYING file in the top folder.
    19  * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
    20  */
    21 package org.apidesign.html.boot.impl;
    22 
    23 import java.io.InputStream;
    24 import java.io.InputStreamReader;
    25 import java.io.Reader;
    26 import java.lang.reflect.Method;
    27 import java.net.URL;
    28 import java.util.ArrayList;
    29 import java.util.Collections;
    30 import java.util.Enumeration;
    31 import java.util.List;
    32 import java.util.Map;
    33 import org.apidesign.html.boot.spi.Fn;
    34 import org.objectweb.asm.Type;
    35 
    36 /**
    37  *
    38  * @author Jaroslav Tulach <jtulach@netbeans.org>
    39  */
    40 public final class FnUtils {
    41     private FnUtils() {
    42     }
    43 
    44     public static Fn define(Class<?> caller, String code, String... names) {
    45         JsClassLoader cl = (JsClassLoader)caller.getClassLoader();
    46         return cl.defineFn(code, names);
    47     }
    48 
    49     public static ClassLoader newLoader(final FindResources f, final Fn.Presenter d, ClassLoader parent) {
    50         return new JsClassLoader(parent) {
    51             @Override
    52             protected URL findResource(String name) {
    53                 List<URL> l = res(name, true);
    54                 return l.isEmpty() ? null : l.get(0);
    55             }
    56             
    57             @Override
    58             protected Enumeration<URL> findResources(String name) {
    59                 return Collections.enumeration(res(name, false));
    60             }
    61             
    62             private List<URL> res(String name, boolean oneIsEnough) {
    63                 List<URL> l = new ArrayList<URL>();
    64                 f.findResources(name, l, oneIsEnough);
    65                 return l;
    66             }
    67             
    68             @Override
    69             protected Fn defineFn(String code, String... names) {
    70                 return d.defineFn(code, names);
    71             }
    72 
    73             @Override
    74             protected void loadScript(Reader code) throws Exception {
    75                 d.loadScript(code);
    76             }
    77         };
    78     }
    79 
    80     static String callback(String body, ClassLoader loader, String ownName, Map<String,String> ownMethods) {
    81         try {
    82             return callbackImpl(body, loader, ownName, ownMethods);
    83         } catch (ClassNotFoundException ex) {
    84             throw new IllegalStateException("Can't parse " + body, ex);
    85         } catch (NoSuchMethodException ex) {
    86             throw new IllegalStateException("Can't parse " + body, ex);
    87         }
    88     }
    89     
    90     private static String callbackImpl(
    91         String body, ClassLoader loader, String ownName, Map<String,String> ownMethods
    92     ) throws ClassNotFoundException, NoSuchMethodException {
    93         StringBuilder sb = new StringBuilder();
    94         int pos = 0;
    95         for (;;) {
    96             int next = body.indexOf(".@", pos);
    97             if (next == -1) {
    98                 sb.append(body.substring(pos));
    99                 return sb.toString();
   100             }
   101             sb.append(body.substring(pos, next));
   102             
   103             int sigBeg = body.indexOf('(', next);
   104             int sigEnd = body.indexOf(')', sigBeg);
   105             
   106             int colon4 = body.indexOf("::", next);
   107             
   108             if (sigBeg == -1 || sigEnd == -1 || colon4 == -1) {
   109                 throw new IllegalStateException("Malformed body " + body);
   110             }
   111             
   112             String fqn = body.substring(next + 2, colon4);
   113             String method = body.substring(colon4 + 2, sigBeg);
   114             String params = body.substring(sigBeg, sigEnd + 1);
   115             
   116             if (fqn.equals(ownName.replace('/', '.'))) {
   117                 if (!ownMethods.containsKey(method + params)) {
   118                     throw new IllegalStateException("Wrong refernece to " + method + params);
   119                 }
   120                 sb.append("['").append(method).append("(");
   121                 final Type[] argTps = Type.getArgumentTypes(params);
   122                 Class<?>[] argCls = new Class<?>[argTps.length];
   123                 String sep = "";
   124                 for (int i = 0; i < argCls.length; i++) {
   125                     sb.append(sep).append(toClass(argTps[i], loader).getName());
   126                     sep = ",";
   127                 }
   128                 sb.append(")']");
   129             } else {
   130                 Class<?> clazz = Class.forName(fqn, false, loader);
   131                 final Type[] argTps = Type.getArgumentTypes(params);
   132                 Class<?>[] argCls = new Class<?>[argTps.length];
   133                 for (int i = 0; i < argCls.length; i++) {
   134                     argCls[i] = toClass(argTps[i], loader);
   135                 }
   136                 Method m = clazz.getMethod(method, argCls);
   137 
   138                 sb.append("['").append(m.getName()).append("(");
   139                 String sep = "";
   140                 for (Class<?> pt : m.getParameterTypes()) {
   141                     sb.append(sep).append(pt.getName());
   142                     sep = ",";
   143                 }
   144                 sb.append(")']");
   145             }
   146             
   147             pos = sigEnd + 1;
   148         }
   149     }
   150 
   151     private static Class<?> toClass(final Type t, ClassLoader loader) throws ClassNotFoundException {
   152         if (t == Type.INT_TYPE) {
   153             return Integer.TYPE;
   154         } else if (t == Type.VOID_TYPE) {
   155             return Void.TYPE;
   156         } else if (t == Type.BOOLEAN_TYPE) {
   157             return Boolean.TYPE;
   158         } else if (t == Type.BYTE_TYPE) {
   159             return Byte.TYPE;
   160         } else if (t == Type.CHAR_TYPE) {
   161             return Character.TYPE;
   162         } else if (t == Type.SHORT_TYPE) {
   163             return Short.TYPE;
   164         } else if (t == Type.DOUBLE_TYPE) {
   165             return Double.TYPE;
   166         } else if (t == Type.FLOAT_TYPE) {
   167             return Float.TYPE;
   168         } else if (t == Type.LONG_TYPE) {
   169             return Long.TYPE;
   170         }
   171         return Class.forName(t.getClassName(), false, loader);
   172     }
   173 
   174     static void loadScript(JsClassLoader jcl, String resource) {
   175         final InputStream script = jcl.getResourceAsStream(resource);
   176         if (script == null) {
   177             throw new NullPointerException("Can't find " + resource);
   178         }
   179         try {
   180             Reader isr = null;
   181             try {
   182                 isr = new InputStreamReader(script, "UTF-8");
   183                 jcl.loadScript(isr);
   184             } finally {
   185                 if (isr != null) {
   186                     isr.close();
   187                 }
   188             }
   189         } catch (Exception ex) {
   190             throw new IllegalStateException("Can't execute " + resource, ex);
   191         } 
   192     }
   193 }