geo/src/main/java/org/netbeans/html/geo/impl/GeoProcessor.java
author Jaroslav Tulach <jaroslav.tulach@netbeans.org>
Fri, 07 Feb 2014 07:44:34 +0100
changeset 551 7ca2253fa86d
parent 534 15907d1499fb
child 790 30f20d9c0986
permissions -rw-r--r--
Updating copyright headers to mention current year
jtulach@0
     1
/**
jaroslav@358
     2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
jtulach@0
     3
 *
jaroslav@551
     4
 * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
jtulach@0
     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.
jtulach@0
     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.
jtulach@0
    42
 */
jaroslav@362
    43
package org.netbeans.html.geo.impl;
jtulach@0
    44
jtulach@0
    45
import java.io.IOException;
jtulach@0
    46
import java.io.Writer;
jaroslav@212
    47
import java.util.List;
jaroslav@172
    48
import java.util.Locale;
jtulach@0
    49
import java.util.Set;
jaroslav@71
    50
import java.util.logging.Level;
jaroslav@70
    51
import java.util.logging.Logger;
jtulach@0
    52
import javax.annotation.processing.AbstractProcessor;
jtulach@0
    53
import javax.annotation.processing.Processor;
jtulach@0
    54
import javax.annotation.processing.RoundEnvironment;
jtulach@0
    55
import javax.annotation.processing.SupportedAnnotationTypes;
jaroslav@58
    56
import javax.annotation.processing.SupportedSourceVersion;
jaroslav@58
    57
import javax.lang.model.SourceVersion;
jtulach@0
    58
import javax.lang.model.element.Element;
jtulach@0
    59
import javax.lang.model.element.ElementKind;
jtulach@0
    60
import javax.lang.model.element.ExecutableElement;
jtulach@0
    61
import javax.lang.model.element.Modifier;
jtulach@0
    62
import javax.lang.model.element.PackageElement;
jtulach@0
    63
import javax.lang.model.element.TypeElement;
jaroslav@212
    64
import javax.lang.model.element.VariableElement;
jaroslav@176
    65
import javax.lang.model.type.TypeMirror;
jtulach@0
    66
import javax.tools.Diagnostic;
jaroslav@172
    67
import javax.tools.JavaFileObject;
jaroslav@172
    68
import net.java.html.geo.OnLocation;
jaroslav@176
    69
import net.java.html.geo.Position;
jaroslav@534
    70
import net.java.html.geo.Position.Handle;
jtulach@0
    71
import org.openide.util.lookup.ServiceProvider;
jtulach@0
    72
jaroslav@534
    73
/** Annotation processor to generate callbacks from {@link Handle} class.
jtulach@0
    74
 *
jtulach@0
    75
 * @author Jaroslav Tulach <jtulach@netbeans.org>
jtulach@0
    76
 */
jtulach@0
    77
@ServiceProvider(service=Processor.class)
jaroslav@100
    78
@SupportedSourceVersion(SourceVersion.RELEASE_6)
jtulach@0
    79
@SupportedAnnotationTypes({
jaroslav@172
    80
    "net.java.html.geo.OnLocation"
jtulach@0
    81
})
jaroslav@172
    82
public final class GeoProcessor extends AbstractProcessor {
jaroslav@172
    83
    private static final Logger LOG = Logger.getLogger(GeoProcessor.class.getName());
jtulach@0
    84
    @Override
jtulach@0
    85
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
jtulach@0
    86
        boolean ok = true;
jaroslav@172
    87
        for (Element e : roundEnv.getElementsAnnotatedWith(OnLocation.class)) {
jaroslav@172
    88
            if (!processLocation(e)) {
jtulach@0
    89
                ok = false;
jtulach@0
    90
            }
jtulach@0
    91
        }
jtulach@0
    92
        return ok;
jtulach@0
    93
    }
jtulach@0
    94
jtulach@0
    95
    private void error(String msg, Element e) {
jtulach@0
    96
        processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, e);
jtulach@0
    97
    }
jtulach@0
    98
    
jaroslav@172
    99
    private boolean processLocation(Element e) {
jaroslav@172
   100
        if (e.getKind() != ElementKind.METHOD) {
jaroslav@172
   101
            return false;
jaroslav@172
   102
        }
jaroslav@172
   103
        ExecutableElement me = (ExecutableElement) e;
jaroslav@172
   104
        OnLocation ol = e.getAnnotation(OnLocation.class);
jaroslav@172
   105
        if (ol == null) {
jtulach@0
   106
            return true;
jtulach@0
   107
        }
jaroslav@176
   108
        if (me.getModifiers().contains(Modifier.PRIVATE)) {
jaroslav@176
   109
            error("Method annotated by @OnLocation cannot be private", e);
jaroslav@176
   110
            return false;
jaroslav@176
   111
        }
jaroslav@176
   112
        TypeMirror positionClass = processingEnv.getElementUtils().getTypeElement(Position.class.getName()).asType();
jaroslav@212
   113
        final List<? extends VariableElement> params = me.getParameters();
jaroslav@212
   114
        if (params.size() < 1 || !params.get(0).asType().equals(positionClass)) {
jaroslav@212
   115
            error("Method annotated by @OnLocation first argument must be net.java.html.geo.Position!", e);
jaroslav@176
   116
            return false;
jaroslav@176
   117
        }
jaroslav@172
   118
        String className = ol.className();
jaroslav@172
   119
        if (className.isEmpty()) {
jaroslav@172
   120
            String n = e.getSimpleName().toString();
jaroslav@172
   121
            if (n.isEmpty()) {
jaroslav@172
   122
                error("Empty method name", e);
jaroslav@172
   123
                return false;
jaroslav@172
   124
            }
jaroslav@172
   125
            final String firstLetter = n.substring(0, 1).toUpperCase(Locale.ENGLISH);
jaroslav@172
   126
            className = firstLetter + n.substring(1) + "Handle";
jaroslav@172
   127
        }
jaroslav@172
   128
        TypeElement te = (TypeElement)e.getEnclosingElement();
jaroslav@172
   129
        PackageElement pe = (PackageElement) te.getEnclosingElement();
jaroslav@172
   130
        final String pkg = pe.getQualifiedName().toString();
jaroslav@172
   131
        final String fqn = pkg + "." + className;
jaroslav@172
   132
        final boolean isStatic = me.getModifiers().contains(Modifier.STATIC);
jaroslav@212
   133
        String sep;
jtulach@0
   134
        try {
jaroslav@172
   135
            JavaFileObject fo = processingEnv.getFiler().createSourceFile(fqn, e);
jaroslav@172
   136
            Writer w = fo.openWriter();
jaroslav@172
   137
            w.append("package ").append(pkg).append(";\n");
jaroslav@175
   138
            w.append("class ").append(className).append(" extends net.java.html.geo.Position.Handle {\n");
jaroslav@212
   139
            if (!isStatic) {
jaroslav@212
   140
                w.append("  private final ").append(te.getSimpleName()).append(" $i;\n");
jaroslav@212
   141
            }
jaroslav@212
   142
            for (int i = 1; i < params.size(); i++) {
jaroslav@212
   143
                final VariableElement p = params.get(i);
jaroslav@212
   144
                w.append("  private final ").append(p.asType().toString()).append(" ").append(p.getSimpleName()).append(";\n");
jaroslav@212
   145
            }
jaroslav@172
   146
            w.append("  private ").append(className).append("(boolean oneTime");
jaroslav@172
   147
            w.append(", ").append(te.getSimpleName()).append(" i");
jaroslav@212
   148
            for (int i = 1; i < params.size(); i++) {
jaroslav@212
   149
                final VariableElement p = params.get(i);
jaroslav@212
   150
                w.append(", ").append(p.asType().toString()).append(" ").append(p.getSimpleName());
jaroslav@212
   151
            }
jaroslav@172
   152
            w.append(") {\n    super(oneTime);\n");
jaroslav@172
   153
            if (!isStatic) {
jaroslav@212
   154
                w.append("    this.$i = i;\n");
jaroslav@212
   155
            }
jaroslav@212
   156
            for (int i = 1; i < params.size(); i++) {
jaroslav@212
   157
                final VariableElement p = params.get(i);
jaroslav@212
   158
                w.append("  this.").append(p.getSimpleName()).append(" = ").append(p.getSimpleName()).append(";\n");
jtulach@0
   159
            }
jaroslav@172
   160
            w.append("}\n");
jaroslav@175
   161
            w.append("  static net.java.html.geo.Position.Handle createQuery(");
jaroslav@172
   162
            String inst;
jaroslav@172
   163
            if (!isStatic) {
jaroslav@172
   164
                w.append(te.getSimpleName()).append(" instance");
jaroslav@172
   165
                inst = "instance";
jaroslav@212
   166
                sep = ", ";
jaroslav@172
   167
            } else {
jaroslav@172
   168
                inst = "null";
jaroslav@212
   169
                sep = "";
jtulach@0
   170
            }
jaroslav@212
   171
            for (int i = 1; i < params.size(); i++) {
jaroslav@212
   172
                final VariableElement p = params.get(i);
jaroslav@212
   173
                w.append(sep).append(p.asType().toString()).append(" ").append(p.getSimpleName());
jaroslav@212
   174
                sep = ", ";
jaroslav@212
   175
            }
jaroslav@212
   176
            w.append(") { return new ").append(className).append("(true, ").append(inst);
jaroslav@212
   177
            for (int i = 1; i < params.size(); i++) {
jaroslav@212
   178
                final VariableElement p = params.get(i);
jaroslav@212
   179
                w.append(", ").append(p.getSimpleName());
jaroslav@212
   180
            }
jaroslav@212
   181
            w.append("); }\n");
jaroslav@175
   182
            w.append("  static net.java.html.geo.Position.Handle createWatch(");
jaroslav@172
   183
            if (!isStatic) {
jaroslav@172
   184
                w.append(te.getSimpleName()).append(" instance");
jaroslav@212
   185
                sep = ", ";
jaroslav@212
   186
            } else {
jaroslav@212
   187
                sep = "";
jtulach@0
   188
            }
jaroslav@212
   189
            for (int i = 1; i < params.size(); i++) {
jaroslav@212
   190
                final VariableElement p = params.get(i);
jaroslav@212
   191
                w.append(sep).append(p.asType().toString()).append(" ").append(p.getSimpleName());
jaroslav@212
   192
            }
jaroslav@212
   193
            w.append(") { return new ").append(className).append("(false, ").append(inst);
jaroslav@212
   194
            for (int i = 1; i < params.size(); i++) {
jaroslav@212
   195
                final VariableElement p = params.get(i);
jaroslav@212
   196
                w.append(", ").append(p.getSimpleName());
jaroslav@212
   197
            }
jaroslav@212
   198
            w.append("); }\n");
jaroslav@180
   199
            w.append("  @Override protected void onError(Exception t) throws Throwable {\n");
jaroslav@174
   200
            if (ol.onError().isEmpty()) {
jaroslav@174
   201
                w.append("    t.printStackTrace();");
jaroslav@174
   202
            } else {
jaroslav@177
   203
                if (!findOnError(me, te, ol.onError(), isStatic)) {
jaroslav@177
   204
                    return false;
jaroslav@177
   205
                }
jaroslav@174
   206
                if (isStatic) {
jaroslav@174
   207
                    w.append("    ").append(te.getSimpleName()).append(".");
jaroslav@174
   208
                } else {
jaroslav@212
   209
                    w.append("    $i.");
jaroslav@174
   210
                }
jaroslav@212
   211
                w.append(ol.onError()).append("(t");
jaroslav@212
   212
                for (int i = 1; i < params.size(); i++) {
jaroslav@212
   213
                    final VariableElement p = params.get(i);
jaroslav@212
   214
                    w.append(", ").append(p.getSimpleName());
jaroslav@212
   215
                }
jaroslav@212
   216
                w.append(");\n");
jaroslav@174
   217
            }
jaroslav@174
   218
            w.append("  }\n");
jaroslav@180
   219
            w.append("  @Override protected void onLocation(net.java.html.geo.Position p) throws Throwable {\n");
jaroslav@173
   220
            if (isStatic) {
jaroslav@173
   221
                w.append("    ").append(te.getSimpleName()).append(".");
jaroslav@173
   222
            } else {
jaroslav@212
   223
                w.append("    $i.");
jaroslav@173
   224
            }
jaroslav@212
   225
            w.append(me.getSimpleName()).append("(p");
jaroslav@212
   226
            for (int i = 1; i < params.size(); i++) {
jaroslav@212
   227
                final VariableElement p = params.get(i);
jaroslav@212
   228
                w.append(", ").append(p.getSimpleName());
jaroslav@212
   229
            }
jaroslav@212
   230
            w.append(");\n");
jaroslav@173
   231
            w.append("  }\n");
jaroslav@172
   232
            w.append("}\n");
jaroslav@172
   233
            w.close();
jtulach@0
   234
        } catch (IOException ex) {
jaroslav@172
   235
            Logger.getLogger(GeoProcessor.class.getName()).log(Level.SEVERE, null, ex);
jaroslav@172
   236
            error("Can't write handler class: " + ex.getMessage(), e);
jtulach@0
   237
            return false;
jtulach@0
   238
        }
jtulach@0
   239
        
jtulach@0
   240
        return true;
jtulach@0
   241
    }
jaroslav@177
   242
jaroslav@212
   243
    private boolean findOnError(ExecutableElement errElem, TypeElement te, String name, boolean onlyStatic) {
jaroslav@177
   244
        String err = null;
jaroslav@212
   245
        METHODS: for (Element e : te.getEnclosedElements()) {
jaroslav@177
   246
            if (e.getKind() != ElementKind.METHOD) {
jaroslav@177
   247
                continue;
jaroslav@177
   248
            }
jaroslav@177
   249
            if (!e.getSimpleName().contentEquals(name)) {
jaroslav@177
   250
                continue;
jaroslav@177
   251
            }
jaroslav@177
   252
            if (onlyStatic && !e.getModifiers().contains(Modifier.STATIC)) {
jaroslav@212
   253
                errElem = (ExecutableElement) e;
jaroslav@177
   254
                err = "Would have to be static";
jaroslav@177
   255
                continue;
jaroslav@177
   256
            }
jaroslav@177
   257
            ExecutableElement ee = (ExecutableElement) e;
jaroslav@177
   258
            TypeMirror excType = processingEnv.getElementUtils().getTypeElement(Exception.class.getName()).asType();
jaroslav@212
   259
            final List<? extends VariableElement> params = ee.getParameters(); 
jaroslav@212
   260
            if (params.size() < 1 || 
jaroslav@177
   261
                !processingEnv.getTypeUtils().isAssignable(excType, ee.getParameters().get(0).asType())
jaroslav@177
   262
            ) {
jaroslav@212
   263
                errElem = (ExecutableElement) e;
jaroslav@212
   264
                err = "Error method first argument needs to be Exception";
jaroslav@177
   265
                continue;
jaroslav@177
   266
            }
jaroslav@212
   267
            final List<? extends Element> origParams = errElem.getParameters();
jaroslav@212
   268
            if (params.size() != origParams.size()) {
jaroslav@212
   269
                errElem = (ExecutableElement) e;
jaroslav@212
   270
                err = "Error method must have the same parameters as @OnLocation one";
jaroslav@212
   271
                continue;
jaroslav@212
   272
            }
jaroslav@212
   273
            for (int i = 1; i < origParams.size(); i++) {
jaroslav@212
   274
                final TypeMirror t1 = params.get(i).asType();
jaroslav@212
   275
                final TypeMirror t2 = origParams.get(i).asType();
jaroslav@212
   276
                if (!processingEnv.getTypeUtils().isSameType(t1, t2)) {
jaroslav@212
   277
                    errElem = (ExecutableElement) e;
jaroslav@212
   278
                    err = "Error method must have the same parameters as @OnLocation one";
jaroslav@212
   279
                    continue METHODS;
jaroslav@212
   280
                }
jaroslav@212
   281
            }
jaroslav@177
   282
            return true;
jaroslav@177
   283
        }
jaroslav@177
   284
        if (err == null) {
jaroslav@177
   285
            err = "Cannot find " + name + "(Exception) method in this class";
jaroslav@177
   286
        }
jaroslav@177
   287
        error(err, errElem);
jaroslav@177
   288
        return false;
jaroslav@177
   289
    }
jtulach@0
   290
}