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
     1 /**
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     5  *
     6  * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
     7  * Other names may be trademarks of their respective owners.
     8  *
     9  * The contents of this file are subject to the terms of either the GNU
    10  * General Public License Version 2 only ("GPL") or the Common
    11  * Development and Distribution License("CDDL") (collectively, the
    12  * "License"). You may not use this file except in compliance with the
    13  * License. You can obtain a copy of the License at
    14  * http://www.netbeans.org/cddl-gplv2.html
    15  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    16  * specific language governing permissions and limitations under the
    17  * License.  When distributing the software, include this License Header
    18  * Notice in each file and include the License file at
    19  * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    20  * particular file as subject to the "Classpath" exception as provided
    21  * by Oracle in the GPL Version 2 section of the License file that
    22  * accompanied this code. If applicable, add the following below the
    23  * License Header, with the fields enclosed by brackets [] replaced by
    24  * your own identifying information:
    25  * "Portions Copyrighted [year] [name of copyright owner]"
    26  *
    27  * Contributor(s):
    28  *
    29  * The Original Software is NetBeans. The Initial Developer of the Original
    30  * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    31  *
    32  * If you wish your version of this file to be governed by only the CDDL
    33  * or only the GPL Version 2, indicate your decision by adding
    34  * "[Contributor] elects to include this software in this distribution
    35  * under the [CDDL or GPL Version 2] license." If you do not indicate a
    36  * single choice of license, a recipient has the option to distribute
    37  * your version of this file under either the CDDL, the GPL Version 2 or
    38  * to extend the choice of license to its licensees as provided above.
    39  * However, if you add GPL Version 2 code and therefore, elected the GPL
    40  * Version 2 license, then the option applies only if the new code is
    41  * made subject to such option by the copyright holder.
    42  */
    43 package org.netbeans.html.geo.impl;
    44 
    45 import java.io.IOException;
    46 import java.io.Writer;
    47 import java.util.List;
    48 import java.util.Locale;
    49 import java.util.Set;
    50 import java.util.logging.Level;
    51 import java.util.logging.Logger;
    52 import javax.annotation.processing.AbstractProcessor;
    53 import javax.annotation.processing.Processor;
    54 import javax.annotation.processing.RoundEnvironment;
    55 import javax.annotation.processing.SupportedAnnotationTypes;
    56 import javax.annotation.processing.SupportedSourceVersion;
    57 import javax.lang.model.SourceVersion;
    58 import javax.lang.model.element.Element;
    59 import javax.lang.model.element.ElementKind;
    60 import javax.lang.model.element.ExecutableElement;
    61 import javax.lang.model.element.Modifier;
    62 import javax.lang.model.element.PackageElement;
    63 import javax.lang.model.element.TypeElement;
    64 import javax.lang.model.element.VariableElement;
    65 import javax.lang.model.type.TypeMirror;
    66 import javax.tools.Diagnostic;
    67 import javax.tools.JavaFileObject;
    68 import net.java.html.geo.OnLocation;
    69 import net.java.html.geo.Position;
    70 import net.java.html.geo.Position.Handle;
    71 import org.openide.util.lookup.ServiceProvider;
    72 
    73 /** Annotation processor to generate callbacks from {@link Handle} class.
    74  *
    75  * @author Jaroslav Tulach <jtulach@netbeans.org>
    76  */
    77 @ServiceProvider(service=Processor.class)
    78 @SupportedSourceVersion(SourceVersion.RELEASE_6)
    79 @SupportedAnnotationTypes({
    80     "net.java.html.geo.OnLocation"
    81 })
    82 public final class GeoProcessor extends AbstractProcessor {
    83     private static final Logger LOG = Logger.getLogger(GeoProcessor.class.getName());
    84     @Override
    85     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    86         boolean ok = true;
    87         for (Element e : roundEnv.getElementsAnnotatedWith(OnLocation.class)) {
    88             if (!processLocation(e)) {
    89                 ok = false;
    90             }
    91         }
    92         return ok;
    93     }
    94 
    95     private void error(String msg, Element e) {
    96         processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, e);
    97     }
    98     
    99     private boolean processLocation(Element e) {
   100         if (e.getKind() != ElementKind.METHOD) {
   101             return false;
   102         }
   103         ExecutableElement me = (ExecutableElement) e;
   104         OnLocation ol = e.getAnnotation(OnLocation.class);
   105         if (ol == null) {
   106             return true;
   107         }
   108         if (me.getModifiers().contains(Modifier.PRIVATE)) {
   109             error("Method annotated by @OnLocation cannot be private", e);
   110             return false;
   111         }
   112         TypeMirror positionClass = processingEnv.getElementUtils().getTypeElement(Position.class.getName()).asType();
   113         final List<? extends VariableElement> params = me.getParameters();
   114         if (params.size() < 1 || !params.get(0).asType().equals(positionClass)) {
   115             error("Method annotated by @OnLocation first argument must be net.java.html.geo.Position!", e);
   116             return false;
   117         }
   118         String className = ol.className();
   119         if (className.isEmpty()) {
   120             String n = e.getSimpleName().toString();
   121             if (n.isEmpty()) {
   122                 error("Empty method name", e);
   123                 return false;
   124             }
   125             final String firstLetter = n.substring(0, 1).toUpperCase(Locale.ENGLISH);
   126             className = firstLetter + n.substring(1) + "Handle";
   127         }
   128         TypeElement te = (TypeElement)e.getEnclosingElement();
   129         PackageElement pe = (PackageElement) te.getEnclosingElement();
   130         final String pkg = pe.getQualifiedName().toString();
   131         final String fqn = pkg + "." + className;
   132         final boolean isStatic = me.getModifiers().contains(Modifier.STATIC);
   133         String sep;
   134         try {
   135             JavaFileObject fo = processingEnv.getFiler().createSourceFile(fqn, e);
   136             Writer w = fo.openWriter();
   137             w.append("package ").append(pkg).append(";\n");
   138             w.append("class ").append(className).append(" extends net.java.html.geo.Position.Handle {\n");
   139             if (!isStatic) {
   140                 w.append("  private final ").append(te.getSimpleName()).append(" $i;\n");
   141             }
   142             for (int i = 1; i < params.size(); i++) {
   143                 final VariableElement p = params.get(i);
   144                 w.append("  private final ").append(p.asType().toString()).append(" ").append(p.getSimpleName()).append(";\n");
   145             }
   146             w.append("  private ").append(className).append("(boolean oneTime");
   147             w.append(", ").append(te.getSimpleName()).append(" i");
   148             for (int i = 1; i < params.size(); i++) {
   149                 final VariableElement p = params.get(i);
   150                 w.append(", ").append(p.asType().toString()).append(" ").append(p.getSimpleName());
   151             }
   152             w.append(") {\n    super(oneTime);\n");
   153             if (!isStatic) {
   154                 w.append("    this.$i = i;\n");
   155             }
   156             for (int i = 1; i < params.size(); i++) {
   157                 final VariableElement p = params.get(i);
   158                 w.append("  this.").append(p.getSimpleName()).append(" = ").append(p.getSimpleName()).append(";\n");
   159             }
   160             w.append("}\n");
   161             w.append("  static net.java.html.geo.Position.Handle createQuery(");
   162             String inst;
   163             if (!isStatic) {
   164                 w.append(te.getSimpleName()).append(" instance");
   165                 inst = "instance";
   166                 sep = ", ";
   167             } else {
   168                 inst = "null";
   169                 sep = "";
   170             }
   171             for (int i = 1; i < params.size(); i++) {
   172                 final VariableElement p = params.get(i);
   173                 w.append(sep).append(p.asType().toString()).append(" ").append(p.getSimpleName());
   174                 sep = ", ";
   175             }
   176             w.append(") { return new ").append(className).append("(true, ").append(inst);
   177             for (int i = 1; i < params.size(); i++) {
   178                 final VariableElement p = params.get(i);
   179                 w.append(", ").append(p.getSimpleName());
   180             }
   181             w.append("); }\n");
   182             w.append("  static net.java.html.geo.Position.Handle createWatch(");
   183             if (!isStatic) {
   184                 w.append(te.getSimpleName()).append(" instance");
   185                 sep = ", ";
   186             } else {
   187                 sep = "";
   188             }
   189             for (int i = 1; i < params.size(); i++) {
   190                 final VariableElement p = params.get(i);
   191                 w.append(sep).append(p.asType().toString()).append(" ").append(p.getSimpleName());
   192             }
   193             w.append(") { return new ").append(className).append("(false, ").append(inst);
   194             for (int i = 1; i < params.size(); i++) {
   195                 final VariableElement p = params.get(i);
   196                 w.append(", ").append(p.getSimpleName());
   197             }
   198             w.append("); }\n");
   199             w.append("  @Override protected void onError(Exception t) throws Throwable {\n");
   200             if (ol.onError().isEmpty()) {
   201                 w.append("    t.printStackTrace();");
   202             } else {
   203                 if (!findOnError(me, te, ol.onError(), isStatic)) {
   204                     return false;
   205                 }
   206                 if (isStatic) {
   207                     w.append("    ").append(te.getSimpleName()).append(".");
   208                 } else {
   209                     w.append("    $i.");
   210                 }
   211                 w.append(ol.onError()).append("(t");
   212                 for (int i = 1; i < params.size(); i++) {
   213                     final VariableElement p = params.get(i);
   214                     w.append(", ").append(p.getSimpleName());
   215                 }
   216                 w.append(");\n");
   217             }
   218             w.append("  }\n");
   219             w.append("  @Override protected void onLocation(net.java.html.geo.Position p) throws Throwable {\n");
   220             if (isStatic) {
   221                 w.append("    ").append(te.getSimpleName()).append(".");
   222             } else {
   223                 w.append("    $i.");
   224             }
   225             w.append(me.getSimpleName()).append("(p");
   226             for (int i = 1; i < params.size(); i++) {
   227                 final VariableElement p = params.get(i);
   228                 w.append(", ").append(p.getSimpleName());
   229             }
   230             w.append(");\n");
   231             w.append("  }\n");
   232             w.append("}\n");
   233             w.close();
   234         } catch (IOException ex) {
   235             Logger.getLogger(GeoProcessor.class.getName()).log(Level.SEVERE, null, ex);
   236             error("Can't write handler class: " + ex.getMessage(), e);
   237             return false;
   238         }
   239         
   240         return true;
   241     }
   242 
   243     private boolean findOnError(ExecutableElement errElem, TypeElement te, String name, boolean onlyStatic) {
   244         String err = null;
   245         METHODS: for (Element e : te.getEnclosedElements()) {
   246             if (e.getKind() != ElementKind.METHOD) {
   247                 continue;
   248             }
   249             if (!e.getSimpleName().contentEquals(name)) {
   250                 continue;
   251             }
   252             if (onlyStatic && !e.getModifiers().contains(Modifier.STATIC)) {
   253                 errElem = (ExecutableElement) e;
   254                 err = "Would have to be static";
   255                 continue;
   256             }
   257             ExecutableElement ee = (ExecutableElement) e;
   258             TypeMirror excType = processingEnv.getElementUtils().getTypeElement(Exception.class.getName()).asType();
   259             final List<? extends VariableElement> params = ee.getParameters(); 
   260             if (params.size() < 1 || 
   261                 !processingEnv.getTypeUtils().isAssignable(excType, ee.getParameters().get(0).asType())
   262             ) {
   263                 errElem = (ExecutableElement) e;
   264                 err = "Error method first argument needs to be Exception";
   265                 continue;
   266             }
   267             final List<? extends Element> origParams = errElem.getParameters();
   268             if (params.size() != origParams.size()) {
   269                 errElem = (ExecutableElement) e;
   270                 err = "Error method must have the same parameters as @OnLocation one";
   271                 continue;
   272             }
   273             for (int i = 1; i < origParams.size(); i++) {
   274                 final TypeMirror t1 = params.get(i).asType();
   275                 final TypeMirror t2 = origParams.get(i).asType();
   276                 if (!processingEnv.getTypeUtils().isSameType(t1, t2)) {
   277                     errElem = (ExecutableElement) e;
   278                     err = "Error method must have the same parameters as @OnLocation one";
   279                     continue METHODS;
   280                 }
   281             }
   282             return true;
   283         }
   284         if (err == null) {
   285             err = "Cannot find " + name + "(Exception) method in this class";
   286         }
   287         error(err, errElem);
   288         return false;
   289     }
   290 }