boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java
author Jaroslav Tulach <jtulach@netbeans.org>
Tue, 26 Aug 2014 18:13:30 +0200
changeset 838 bdc3d696dd4a
parent 790 30f20d9c0986
child 911 3f5a6b1ac510
permissions -rw-r--r--
During the API review process (bug 246133) the reviewers decided that in order to include html4j to NetBeans Platform, we need to stop using org.apidesign namespace and switch to NetBeans one. Repackaging all SPI packages into org.netbeans.html.smthng.spi.
jaroslav@165
     1
/**
jaroslav@358
     2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
jaroslav@165
     3
 *
jaroslav@551
     4
 * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
jaroslav@165
     5
 *
jaroslav@358
     6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
jaroslav@358
     7
 * Other names may be trademarks of their respective owners.
jaroslav@165
     8
 *
jaroslav@358
     9
 * The contents of this file are subject to the terms of either the GNU
jaroslav@358
    10
 * General Public License Version 2 only ("GPL") or the Common
jaroslav@358
    11
 * Development and Distribution License("CDDL") (collectively, the
jaroslav@358
    12
 * "License"). You may not use this file except in compliance with the
jaroslav@358
    13
 * License. You can obtain a copy of the License at
jaroslav@358
    14
 * http://www.netbeans.org/cddl-gplv2.html
jaroslav@358
    15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
jaroslav@358
    16
 * specific language governing permissions and limitations under the
jaroslav@358
    17
 * License.  When distributing the software, include this License Header
jaroslav@358
    18
 * Notice in each file and include the License file at
jaroslav@358
    19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
jaroslav@358
    20
 * particular file as subject to the "Classpath" exception as provided
jaroslav@358
    21
 * by Oracle in the GPL Version 2 section of the License file that
jaroslav@358
    22
 * accompanied this code. If applicable, add the following below the
jaroslav@358
    23
 * License Header, with the fields enclosed by brackets [] replaced by
jaroslav@358
    24
 * your own identifying information:
jaroslav@358
    25
 * "Portions Copyrighted [year] [name of copyright owner]"
jaroslav@358
    26
 *
jaroslav@358
    27
 * Contributor(s):
jaroslav@358
    28
 *
jaroslav@358
    29
 * The Original Software is NetBeans. The Initial Developer of the Original
jaroslav@551
    30
 * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
jaroslav@358
    31
 *
jaroslav@358
    32
 * If you wish your version of this file to be governed by only the CDDL
jaroslav@358
    33
 * or only the GPL Version 2, indicate your decision by adding
jaroslav@358
    34
 * "[Contributor] elects to include this software in this distribution
jaroslav@358
    35
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
jaroslav@358
    36
 * single choice of license, a recipient has the option to distribute
jaroslav@358
    37
 * your version of this file under either the CDDL, the GPL Version 2 or
jaroslav@358
    38
 * to extend the choice of license to its licensees as provided above.
jaroslav@358
    39
 * However, if you add GPL Version 2 code and therefore, elected the GPL
jaroslav@358
    40
 * Version 2 license, then the option applies only if the new code is
jaroslav@358
    41
 * made subject to such option by the copyright holder.
jaroslav@165
    42
 */
jaroslav@362
    43
package org.netbeans.html.boot.impl;
jaroslav@165
    44
jaroslav@186
    45
import java.io.IOException;
jaroslav@459
    46
import java.io.OutputStream;
jaroslav@459
    47
import java.io.OutputStreamWriter;
jaroslav@459
    48
import java.io.PrintWriter;
jaroslav@186
    49
import java.io.Writer;
jaroslav@165
    50
import java.util.Collections;
jaroslav@186
    51
import java.util.HashMap;
jaroslav@165
    52
import java.util.HashSet;
jaroslav@165
    53
import java.util.List;
jaroslav@184
    54
import java.util.Map;
jaroslav@165
    55
import java.util.Set;
jaroslav@186
    56
import java.util.TreeMap;
jaroslav@165
    57
import javax.annotation.processing.AbstractProcessor;
jaroslav@165
    58
import javax.annotation.processing.Completion;
jaroslav@165
    59
import javax.annotation.processing.Completions;
jaroslav@165
    60
import javax.annotation.processing.Messager;
jaroslav@165
    61
import javax.annotation.processing.Processor;
jaroslav@165
    62
import javax.annotation.processing.RoundEnvironment;
jtulach@327
    63
import javax.lang.model.SourceVersion;
jaroslav@165
    64
import javax.lang.model.element.AnnotationMirror;
jaroslav@165
    65
import javax.lang.model.element.Element;
jaroslav@165
    66
import javax.lang.model.element.ElementKind;
jaroslav@165
    67
import javax.lang.model.element.ExecutableElement;
jaroslav@186
    68
import javax.lang.model.element.Modifier;
jaroslav@509
    69
import javax.lang.model.element.Name;
jaroslav@186
    70
import javax.lang.model.element.PackageElement;
jaroslav@165
    71
import javax.lang.model.element.TypeElement;
jaroslav@165
    72
import javax.lang.model.element.VariableElement;
jaroslav@185
    73
import javax.lang.model.type.ArrayType;
jaroslav@185
    74
import javax.lang.model.type.ExecutableType;
jaroslav@185
    75
import javax.lang.model.type.TypeKind;
jaroslav@185
    76
import javax.lang.model.type.TypeMirror;
jaroslav@165
    77
import javax.tools.Diagnostic;
jaroslav@348
    78
import javax.tools.FileObject;
jaroslav@348
    79
import javax.tools.StandardLocation;
jaroslav@165
    80
import net.java.html.js.JavaScriptBody;
jaroslav@165
    81
import net.java.html.js.JavaScriptResource;
jaroslav@165
    82
import org.openide.util.lookup.ServiceProvider;
jaroslav@165
    83
jaroslav@165
    84
/**
jaroslav@165
    85
 *
jtulach@790
    86
 * @author Jaroslav Tulach
jaroslav@165
    87
 */
jaroslav@165
    88
@ServiceProvider(service = Processor.class)
jaroslav@165
    89
public final class JavaScriptProcesor extends AbstractProcessor {
jaroslav@186
    90
    private final Map<String,Map<String,ExecutableElement>> javacalls = 
jaroslav@186
    91
        new HashMap<String,Map<String,ExecutableElement>>();
jaroslav@459
    92
    private final Map<String,Set<TypeElement>> bodies = 
jaroslav@459
    93
        new HashMap<String, Set<TypeElement>>();
jaroslav@186
    94
    
jaroslav@165
    95
    @Override
jaroslav@165
    96
    public Set<String> getSupportedAnnotationTypes() {
jaroslav@165
    97
        Set<String> set = new HashSet<String>();
jaroslav@165
    98
        set.add(JavaScriptBody.class.getName());
jaroslav@165
    99
        set.add(JavaScriptResource.class.getName());
jaroslav@165
   100
        return set;
jaroslav@165
   101
    }
jaroslav@165
   102
    
jaroslav@165
   103
    @Override
jaroslav@165
   104
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
jaroslav@165
   105
        final Messager msg = processingEnv.getMessager();
jaroslav@165
   106
        for (Element e : roundEnv.getElementsAnnotatedWith(JavaScriptBody.class)) {
jaroslav@165
   107
            if (e.getKind() != ElementKind.METHOD && e.getKind() != ElementKind.CONSTRUCTOR) {
jaroslav@165
   108
                continue;
jaroslav@165
   109
            }
jaroslav@165
   110
            ExecutableElement ee = (ExecutableElement)e;
jaroslav@165
   111
            List<? extends VariableElement> params = ee.getParameters();
jaroslav@165
   112
            
jaroslav@165
   113
            JavaScriptBody jsb = e.getAnnotation(JavaScriptBody.class);
jaroslav@165
   114
            if (jsb == null) {
jaroslav@165
   115
                continue;
jaroslav@459
   116
            } else {
jaroslav@459
   117
                Set<TypeElement> classes = this.bodies.get(findPkg(e));
jaroslav@459
   118
                if (classes == null) {
jaroslav@459
   119
                    classes = new HashSet<TypeElement>();
jaroslav@459
   120
                    bodies.put(findPkg(e), classes);
jaroslav@459
   121
                }
jaroslav@459
   122
                Element t = e.getEnclosingElement();
jaroslav@459
   123
                while (!t.getKind().isClass() && !t.getKind().isInterface()) {
jaroslav@459
   124
                    t = t.getEnclosingElement();
jaroslav@459
   125
                }
jaroslav@459
   126
                classes.add((TypeElement)t);
jaroslav@165
   127
            }
jaroslav@165
   128
            String[] arr = jsb.args();
jaroslav@165
   129
            if (params.size() != arr.length) {
jaroslav@165
   130
                msg.printMessage(Diagnostic.Kind.ERROR, "Number of args arguments does not match real arguments!", e);
jaroslav@165
   131
            }
jaroslav@570
   132
            if (!jsb.wait4js() && ee.getReturnType().getKind() != TypeKind.VOID) {
jaroslav@570
   133
                msg.printMessage(Diagnostic.Kind.ERROR, "Methods that don't wait for JavaScript to finish must return void!", e);
jaroslav@570
   134
            }
jaroslav@165
   135
            if (!jsb.javacall() && jsb.body().contains(".@")) {
jaroslav@165
   136
                msg.printMessage(Diagnostic.Kind.WARNING, "Usage of .@ usually requires javacall=true", e);
jaroslav@165
   137
            }
jaroslav@184
   138
            if (jsb.javacall()) {
jaroslav@184
   139
                JsCallback verify = new VerifyCallback(e);
jaroslav@273
   140
                try {
jaroslav@273
   141
                    verify.parse(jsb.body());
jaroslav@273
   142
                } catch (IllegalStateException ex) {
jaroslav@273
   143
                    msg.printMessage(Diagnostic.Kind.ERROR, ex.getLocalizedMessage(), e);
jaroslav@273
   144
                }
jaroslav@184
   145
            }
jaroslav@165
   146
        }
jaroslav@348
   147
        for (Element e : roundEnv.getElementsAnnotatedWith(JavaScriptResource.class)) {
jaroslav@348
   148
            JavaScriptResource r = e.getAnnotation(JavaScriptResource.class);
jaroslav@348
   149
            if (r == null) {
jaroslav@348
   150
                continue;
jaroslav@348
   151
            }
jaroslav@348
   152
            final String res;
jaroslav@348
   153
            if (r.value().startsWith("/")) {
jaroslav@676
   154
                res = r.value().substring(1);
jaroslav@348
   155
            } else {
jaroslav@348
   156
                res = findPkg(e).replace('.', '/') + "/" + r.value();
jaroslav@348
   157
            }
jaroslav@348
   158
            
jaroslav@348
   159
            try {
jaroslav@348
   160
                FileObject os = processingEnv.getFiler().getResource(StandardLocation.SOURCE_PATH, "", res);
jaroslav@348
   161
                os.openInputStream().close();
jaroslav@348
   162
            } catch (IOException ex1) {
jaroslav@348
   163
                try {
jaroslav@348
   164
                    FileObject os2 = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", res);
jaroslav@348
   165
                    os2.openInputStream().close();
jaroslav@348
   166
                } catch (IOException ex2) {
jaroslav@676
   167
                    try {
jaroslav@676
   168
                        FileObject os3 = processingEnv.getFiler().getResource(StandardLocation.CLASS_PATH, "", res);
jaroslav@676
   169
                        os3.openInputStream().close();
jaroslav@676
   170
                    } catch (IOException ex3) {
jaroslav@676
   171
                        msg.printMessage(Diagnostic.Kind.ERROR, "Cannot find resource " + res, e);
jaroslav@676
   172
                    }
jaroslav@348
   173
                }
jaroslav@348
   174
            }
jaroslav@531
   175
            
jaroslav@531
   176
            boolean found = false;
jaroslav@531
   177
            for (Element mthod : e.getEnclosedElements()) {
jaroslav@531
   178
                if (mthod.getKind() != ElementKind.METHOD) {
jaroslav@531
   179
                    continue;
jaroslav@531
   180
                }
jaroslav@531
   181
                if (mthod.getAnnotation(JavaScriptBody.class) != null) {
jaroslav@531
   182
                    found = true;
jaroslav@531
   183
                    break;
jaroslav@531
   184
                }
jaroslav@531
   185
            }
jaroslav@531
   186
            if (!found) {
jaroslav@531
   187
                msg.printMessage(Diagnostic.Kind.ERROR, "At least one method needs @JavaScriptBody annotation. "
jaroslav@531
   188
                    + "Otherwise it is not guaranteed the resource will ever be loaded,", e
jaroslav@531
   189
                );
jaroslav@531
   190
            }
jaroslav@348
   191
        }
jaroslav@348
   192
jaroslav@186
   193
        if (roundEnv.processingOver()) {
jaroslav@186
   194
            generateCallbackClass(javacalls);
jaroslav@459
   195
            generateJavaScriptBodyList(bodies);
jaroslav@186
   196
            javacalls.clear();
jaroslav@186
   197
        }
jaroslav@165
   198
        return true;
jaroslav@165
   199
    }
jaroslav@165
   200
jaroslav@165
   201
    @Override
jaroslav@165
   202
    public Iterable<? extends Completion> getCompletions(Element e, 
jaroslav@165
   203
        AnnotationMirror annotation, ExecutableElement member, String userText
jaroslav@165
   204
    ) {
jaroslav@165
   205
        StringBuilder sb = new StringBuilder();
jaroslav@165
   206
        if (e.getKind() == ElementKind.METHOD && member.getSimpleName().contentEquals("args")) {
jaroslav@165
   207
            ExecutableElement ee = (ExecutableElement) e;
jaroslav@165
   208
            String sep = "";
jaroslav@165
   209
            sb.append("{ ");
jaroslav@165
   210
            for (VariableElement ve : ee.getParameters()) {
jaroslav@165
   211
                sb.append(sep).append('"').append(ve.getSimpleName())
jaroslav@165
   212
                    .append('"');
jaroslav@165
   213
                sep = ", ";
jaroslav@165
   214
            }
jaroslav@165
   215
            sb.append(" }");
jaroslav@165
   216
            return Collections.nCopies(1, Completions.of(sb.toString()));
jaroslav@165
   217
        }
jaroslav@165
   218
        return null;
jaroslav@165
   219
    }
jaroslav@165
   220
jaroslav@184
   221
    private class VerifyCallback extends JsCallback {
jaroslav@184
   222
        private final Element e;
jaroslav@184
   223
        public VerifyCallback(Element e) {
jaroslav@184
   224
            this.e = e;
jaroslav@184
   225
        }
jaroslav@184
   226
jaroslav@184
   227
        @Override
jaroslav@184
   228
        protected CharSequence callMethod(String ident, String fqn, String method, String params) {
jaroslav@185
   229
            final TypeElement type = processingEnv.getElementUtils().getTypeElement(fqn);
jaroslav@185
   230
            if (type == null) {
jaroslav@184
   231
                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, 
jaroslav@184
   232
                    "Callback to non-existing class " + fqn, e
jaroslav@184
   233
                );
jaroslav@185
   234
                return "";
jaroslav@185
   235
            }
jaroslav@186
   236
            ExecutableElement found = null;
jaroslav@185
   237
            StringBuilder foundParams = new StringBuilder();
jaroslav@185
   238
            for (Element m : type.getEnclosedElements()) {
jaroslav@185
   239
                if (m.getKind() != ElementKind.METHOD) {
jaroslav@185
   240
                    continue;
jaroslav@185
   241
                }
jaroslav@185
   242
                if (m.getSimpleName().contentEquals(method)) {
jaroslav@188
   243
                    String paramTypes = findParamTypes((ExecutableElement)m);
jaroslav@185
   244
                    if (paramTypes.equals(params)) {
jaroslav@186
   245
                        found = (ExecutableElement) m;
jaroslav@185
   246
                        break;
jaroslav@185
   247
                    }
jaroslav@185
   248
                    foundParams.append(paramTypes).append("\n");
jaroslav@185
   249
                }
jaroslav@185
   250
            }
jaroslav@185
   251
            if (found == null) {
jaroslav@185
   252
                if (foundParams.length() == 0) {
jaroslav@185
   253
                    processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, 
jaroslav@185
   254
                        "Callback to class " + fqn + " with unknown method " + method, e
jaroslav@185
   255
                    );
jaroslav@185
   256
                } else {
jaroslav@185
   257
                    processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, 
jaroslav@185
   258
                        "Callback to " + fqn + "." + method + " with wrong parameters: " + 
jaroslav@185
   259
                        params + ". Only known parameters are " + foundParams, e
jaroslav@185
   260
                    );
jaroslav@185
   261
                }
jaroslav@186
   262
            } else {
jaroslav@186
   263
                Map<String,ExecutableElement> mangledOnes = javacalls.get(findPkg(e));
jaroslav@186
   264
                if (mangledOnes == null) {
jaroslav@186
   265
                    mangledOnes = new TreeMap<String, ExecutableElement>();
jaroslav@186
   266
                    javacalls.put(findPkg(e), mangledOnes);
jaroslav@186
   267
                }
jaroslav@188
   268
                String mangled = JsCallback.mangle(fqn, method, findParamTypes(found));
jaroslav@186
   269
                mangledOnes.put(mangled, found);
jaroslav@184
   270
            }
jaroslav@184
   271
            return "";
jaroslav@184
   272
        }
jaroslav@185
   273
jaroslav@188
   274
        private String findParamTypes(ExecutableElement method) {
jaroslav@185
   275
            ExecutableType t = (ExecutableType) method.asType();
jaroslav@185
   276
            StringBuilder sb = new StringBuilder();
jaroslav@188
   277
            sb.append('(');
jaroslav@185
   278
            for (TypeMirror tm : t.getParameterTypes()) {
jaroslav@185
   279
                if (tm.getKind().isPrimitive()) {
jaroslav@185
   280
                    switch (tm.getKind()) {
jaroslav@185
   281
                        case INT: sb.append('I'); break;
jaroslav@185
   282
                        case BOOLEAN: sb.append('Z'); break;
jaroslav@185
   283
                        case BYTE: sb.append('B'); break;
jaroslav@185
   284
                        case CHAR: sb.append('C'); break;
jaroslav@185
   285
                        case SHORT: sb.append('S'); break;
jaroslav@185
   286
                        case DOUBLE: sb.append('D'); break;
jaroslav@185
   287
                        case FLOAT: sb.append('F'); break;
jaroslav@185
   288
                        case LONG: sb.append('J'); break;
jaroslav@185
   289
                        default:
jaroslav@185
   290
                            throw new IllegalStateException("Uknown " + tm.getKind());
jaroslav@185
   291
                    }
jaroslav@185
   292
                } else {
jaroslav@185
   293
                    while (tm.getKind() == TypeKind.ARRAY) {
jaroslav@185
   294
                        sb.append('[');
jaroslav@185
   295
                        tm = ((ArrayType)tm).getComponentType();
jaroslav@185
   296
                    }
jaroslav@185
   297
                    sb.append('L');
jaroslav@185
   298
                    sb.append(tm.toString().replace('.', '/'));
jaroslav@185
   299
                    sb.append(';');
jaroslav@185
   300
                }
jaroslav@185
   301
            }
jaroslav@188
   302
            sb.append(')');
jaroslav@185
   303
            return sb.toString();
jaroslav@185
   304
        }
jaroslav@184
   305
    }
jaroslav@186
   306
    
jaroslav@459
   307
    private void generateJavaScriptBodyList(Map<String,Set<TypeElement>> bodies) {
jaroslav@509
   308
        if (bodies.isEmpty()) {
jaroslav@509
   309
            return;
jaroslav@509
   310
        }
jaroslav@509
   311
        try {
jaroslav@509
   312
            FileObject all = processingEnv.getFiler().createResource(
jaroslav@509
   313
                StandardLocation.CLASS_OUTPUT, "", "META-INF/net.java.html.js.classes"                
jaroslav@509
   314
            );
jaroslav@509
   315
            PrintWriter wAll = new PrintWriter(new OutputStreamWriter(
jaroslav@509
   316
                all.openOutputStream(), "UTF-8"
jaroslav@509
   317
            ));
jaroslav@509
   318
            for (Map.Entry<String, Set<TypeElement>> entry : bodies.entrySet()) {
jaroslav@509
   319
                String pkg = entry.getKey();
jaroslav@509
   320
                Set<TypeElement> classes = entry.getValue();
jaroslav@509
   321
jaroslav@459
   322
                FileObject out = processingEnv.getFiler().createResource(
jaroslav@459
   323
                    StandardLocation.CLASS_OUTPUT, pkg, "net.java.html.js.classes",
jaroslav@459
   324
                    classes.iterator().next()
jaroslav@459
   325
                );
jaroslav@459
   326
                OutputStream os = out.openOutputStream();
jaroslav@459
   327
                try {
jaroslav@459
   328
                    PrintWriter w = new PrintWriter(new OutputStreamWriter(os, "UTF-8"));
jaroslav@459
   329
                    for (TypeElement type : classes) {
jaroslav@509
   330
                        final Name bn = processingEnv.getElementUtils().getBinaryName(type);
jaroslav@509
   331
                        w.println(bn);
jaroslav@509
   332
                        wAll.println(bn);
jaroslav@459
   333
                    }
jaroslav@459
   334
                    w.flush();
jaroslav@459
   335
                    w.close();
jaroslav@509
   336
                } catch (IOException x) {
jaroslav@509
   337
                    processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Failed to write to " + entry.getKey() + ": " + x.toString());
jaroslav@459
   338
                } finally {
jaroslav@459
   339
                    os.close();
jaroslav@459
   340
                }
jaroslav@459
   341
            }
jaroslav@509
   342
            wAll.close();
jaroslav@509
   343
        } catch (IOException x) {
jaroslav@509
   344
            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Failed to write to " + "META-INF/net.java.html.js.classes: " + x.toString());
jaroslav@459
   345
        }
jaroslav@459
   346
    }
jaroslav@459
   347
    
jaroslav@186
   348
    private void generateCallbackClass(Map<String,Map<String, ExecutableElement>> process) {
jaroslav@186
   349
        for (Map.Entry<String, Map<String, ExecutableElement>> pkgEn : process.entrySet()) {
jaroslav@186
   350
            String pkgName = pkgEn.getKey();
jaroslav@186
   351
            Map<String, ExecutableElement> map = pkgEn.getValue();
jaroslav@186
   352
            StringBuilder source = new StringBuilder();
jaroslav@186
   353
            source.append("package ").append(pkgName).append(";\n");
jaroslav@188
   354
            source.append("public final class $JsCallbacks$ {\n");
jaroslav@288
   355
            source.append("  static final $JsCallbacks$ VM = new $JsCallbacks$(null);\n");
jtulach@838
   356
            source.append("  private final org.netbeans.html.boot.spi.Fn.Presenter p;\n");
jaroslav@288
   357
            source.append("  private $JsCallbacks$ last;\n");
jtulach@838
   358
            source.append("  private $JsCallbacks$(org.netbeans.html.boot.spi.Fn.Presenter p) {\n");
jaroslav@288
   359
            source.append("    this.p = p;\n");
jaroslav@288
   360
            source.append("  }\n");
jaroslav@288
   361
            source.append("  final $JsCallbacks$ current() {\n");
jtulach@838
   362
            source.append("    org.netbeans.html.boot.spi.Fn.Presenter now = org.netbeans.html.boot.spi.Fn.activePresenter();\n");
jaroslav@288
   363
            source.append("    if (now == p) return this;\n");
jaroslav@288
   364
            source.append("    if (last != null && now == last.p) return last;\n");
jaroslav@288
   365
            source.append("    return last = new $JsCallbacks$(now);\n");
jaroslav@288
   366
            source.append("  }\n");
jaroslav@186
   367
            for (Map.Entry<String, ExecutableElement> entry : map.entrySet()) {
jaroslav@186
   368
                final String mangled = entry.getKey();
jaroslav@186
   369
                final ExecutableElement m = entry.getValue();
jaroslav@186
   370
                final boolean isStatic = m.getModifiers().contains(Modifier.STATIC);
jaroslav@186
   371
                
jaroslav@186
   372
                source.append("\n  public java.lang.Object ")
jaroslav@186
   373
                    .append(mangled)
jaroslav@186
   374
                    .append("(");
jaroslav@186
   375
                
jaroslav@186
   376
                String sep = "";
jaroslav@186
   377
                if (!isStatic) {
jaroslav@186
   378
                    source.append(((TypeElement)m.getEnclosingElement()).getQualifiedName());
jaroslav@186
   379
                    source.append(" self");
jaroslav@186
   380
                    sep = ", ";
jaroslav@186
   381
                }
jaroslav@186
   382
                
jaroslav@186
   383
                int cnt = 0;
jaroslav@446
   384
                StringBuilder convert = new StringBuilder();
jaroslav@186
   385
                for (VariableElement ve : m.getParameters()) {
jaroslav@186
   386
                    source.append(sep);
jaroslav@446
   387
                    ++cnt;
jaroslav@446
   388
                    final TypeMirror t = ve.asType();
jaroslav@446
   389
                    if (!t.getKind().isPrimitive()) {
jaroslav@446
   390
                        source.append("Object");
jtulach@838
   391
                        convert.append("    if (p instanceof org.netbeans.html.boot.spi.Fn.FromJavaScript) {\n");
jaroslav@446
   392
                        convert.append("      arg").append(cnt).
jtulach@838
   393
                            append(" = ((org.netbeans.html.boot.spi.Fn.FromJavaScript)p).toJava(arg").append(cnt).
jaroslav@446
   394
                            append(");\n");
jaroslav@446
   395
                        convert.append("    }\n");
jaroslav@446
   396
                    } else {
jaroslav@446
   397
                        source.append(t);
jaroslav@446
   398
                    }
jaroslav@446
   399
                    source.append(" arg").append(cnt);
jaroslav@186
   400
                    sep = ", ";
jaroslav@186
   401
                }
jaroslav@322
   402
                source.append(") throws Throwable {\n");
jaroslav@446
   403
                source.append(convert);
jaroslav@609
   404
                if (useTryResources()) {
jtulach@838
   405
                    source.append("    try (java.io.Closeable a = org.netbeans.html.boot.spi.Fn.activate(p)) { \n");
jtulach@327
   406
                } else {
jtulach@838
   407
                    source.append("    java.io.Closeable a = org.netbeans.html.boot.spi.Fn.activate(p); try {\n");
jtulach@327
   408
                }
jaroslav@186
   409
                source.append("    ");
jaroslav@186
   410
                if (m.getReturnType().getKind() != TypeKind.VOID) {
jaroslav@430
   411
                    source.append("Object $ret = ");
jaroslav@186
   412
                }
jaroslav@191
   413
                if (isStatic) {
jaroslav@191
   414
                    source.append(((TypeElement)m.getEnclosingElement()).getQualifiedName());
jaroslav@191
   415
                    source.append('.');
jaroslav@191
   416
                } else {
jaroslav@186
   417
                    source.append("self.");
jaroslav@186
   418
                }
jaroslav@186
   419
                source.append(m.getSimpleName());
jaroslav@186
   420
                source.append("(");
jaroslav@186
   421
                cnt = 0;
jaroslav@186
   422
                sep = "";
jaroslav@186
   423
                for (VariableElement ve : m.getParameters()) {
jaroslav@186
   424
                    source.append(sep);
jaroslav@446
   425
                    source.append("(").append(ve.asType());
jaroslav@446
   426
                    source.append(")arg").append(++cnt);
jaroslav@186
   427
                    sep = ", ";
jaroslav@186
   428
                }
jaroslav@186
   429
                source.append(");\n");
jaroslav@186
   430
                if (m.getReturnType().getKind() == TypeKind.VOID) {
jaroslav@186
   431
                    source.append("    return null;\n");
jaroslav@430
   432
                } else {
jtulach@838
   433
                    source.append("    if (p instanceof org.netbeans.html.boot.spi.Fn.ToJavaScript) {\n");
jtulach@838
   434
                    source.append("      $ret = ((org.netbeans.html.boot.spi.Fn.ToJavaScript)p).toJavaScript($ret);\n");
jaroslav@430
   435
                    source.append("    }\n");
jaroslav@430
   436
                    source.append("    return $ret;\n");
jaroslav@186
   437
                }
jaroslav@609
   438
                if (useTryResources()) {
jtulach@327
   439
                    source.append("    }\n");
jtulach@327
   440
                } else {
jtulach@327
   441
                    
jtulach@327
   442
                    source.append("    } finally {\n");
jtulach@327
   443
                    source.append("      a.close();\n");
jtulach@327
   444
                    source.append("    }\n");
jtulach@327
   445
                }
jaroslav@186
   446
                source.append("  }\n");
jaroslav@186
   447
            }
jaroslav@186
   448
            source.append("}\n");
jaroslav@186
   449
            final String srcName = pkgName + ".$JsCallbacks$";
jaroslav@186
   450
            try {
jaroslav@186
   451
                Writer w = processingEnv.getFiler().createSourceFile(srcName,
jaroslav@186
   452
                    map.values().toArray(new Element[map.size()])
jaroslav@186
   453
                ).openWriter();
jaroslav@186
   454
                w.write(source.toString());
jaroslav@186
   455
                w.close();
jaroslav@186
   456
            } catch (IOException ex) {
jaroslav@186
   457
                processingEnv.getMessager().printMessage(
jaroslav@186
   458
                    Diagnostic.Kind.ERROR, "Can't write " + srcName + ": " + ex.getMessage()
jaroslav@186
   459
                );
jaroslav@186
   460
            }
jaroslav@186
   461
        }
jaroslav@186
   462
    }
jaroslav@609
   463
jaroslav@609
   464
    private boolean useTryResources() {
jaroslav@609
   465
        try {
jaroslav@609
   466
            return processingEnv.getSourceVersion().compareTo(SourceVersion.RELEASE_7) >= 0;
jaroslav@609
   467
        } catch (LinkageError err) {
jaroslav@609
   468
            // can happen when running on JDK6
jaroslav@609
   469
            return false;
jaroslav@609
   470
        }
jaroslav@609
   471
    }
jaroslav@186
   472
    
jaroslav@186
   473
    private static String findPkg(Element e) {
jaroslav@186
   474
        while (e.getKind() != ElementKind.PACKAGE) {
jaroslav@186
   475
            e = e.getEnclosingElement();
jaroslav@186
   476
        }
jaroslav@186
   477
        return ((PackageElement)e).getQualifiedName().toString();
jaroslav@186
   478
    }
jaroslav@186
   479
    
jaroslav@165
   480
}