1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/geo/src/main/java/org/netbeans/html/geo/impl/GeoProcessor.java Mon Dec 16 16:59:43 2013 +0100
1.3 @@ -0,0 +1,289 @@
1.4 +/**
1.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
1.6 + *
1.7 + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
1.8 + *
1.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
1.10 + * Other names may be trademarks of their respective owners.
1.11 + *
1.12 + * The contents of this file are subject to the terms of either the GNU
1.13 + * General Public License Version 2 only ("GPL") or the Common
1.14 + * Development and Distribution License("CDDL") (collectively, the
1.15 + * "License"). You may not use this file except in compliance with the
1.16 + * License. You can obtain a copy of the License at
1.17 + * http://www.netbeans.org/cddl-gplv2.html
1.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
1.19 + * specific language governing permissions and limitations under the
1.20 + * License. When distributing the software, include this License Header
1.21 + * Notice in each file and include the License file at
1.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
1.23 + * particular file as subject to the "Classpath" exception as provided
1.24 + * by Oracle in the GPL Version 2 section of the License file that
1.25 + * accompanied this code. If applicable, add the following below the
1.26 + * License Header, with the fields enclosed by brackets [] replaced by
1.27 + * your own identifying information:
1.28 + * "Portions Copyrighted [year] [name of copyright owner]"
1.29 + *
1.30 + * Contributor(s):
1.31 + *
1.32 + * The Original Software is NetBeans. The Initial Developer of the Original
1.33 + * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
1.34 + *
1.35 + * If you wish your version of this file to be governed by only the CDDL
1.36 + * or only the GPL Version 2, indicate your decision by adding
1.37 + * "[Contributor] elects to include this software in this distribution
1.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
1.39 + * single choice of license, a recipient has the option to distribute
1.40 + * your version of this file under either the CDDL, the GPL Version 2 or
1.41 + * to extend the choice of license to its licensees as provided above.
1.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
1.43 + * Version 2 license, then the option applies only if the new code is
1.44 + * made subject to such option by the copyright holder.
1.45 + */
1.46 +package org.netbeans.html.geo.impl;
1.47 +
1.48 +import java.io.IOException;
1.49 +import java.io.Writer;
1.50 +import java.util.List;
1.51 +import java.util.Locale;
1.52 +import java.util.Set;
1.53 +import java.util.logging.Level;
1.54 +import java.util.logging.Logger;
1.55 +import javax.annotation.processing.AbstractProcessor;
1.56 +import javax.annotation.processing.Processor;
1.57 +import javax.annotation.processing.RoundEnvironment;
1.58 +import javax.annotation.processing.SupportedAnnotationTypes;
1.59 +import javax.annotation.processing.SupportedSourceVersion;
1.60 +import javax.lang.model.SourceVersion;
1.61 +import javax.lang.model.element.Element;
1.62 +import javax.lang.model.element.ElementKind;
1.63 +import javax.lang.model.element.ExecutableElement;
1.64 +import javax.lang.model.element.Modifier;
1.65 +import javax.lang.model.element.PackageElement;
1.66 +import javax.lang.model.element.TypeElement;
1.67 +import javax.lang.model.element.VariableElement;
1.68 +import javax.lang.model.type.TypeMirror;
1.69 +import javax.tools.Diagnostic;
1.70 +import javax.tools.JavaFileObject;
1.71 +import net.java.html.geo.OnLocation;
1.72 +import net.java.html.geo.Position;
1.73 +import org.openide.util.lookup.ServiceProvider;
1.74 +
1.75 +/** Annotation processor to generate callbacks from {@link GeoHandle} class.
1.76 + *
1.77 + * @author Jaroslav Tulach <jtulach@netbeans.org>
1.78 + */
1.79 +@ServiceProvider(service=Processor.class)
1.80 +@SupportedSourceVersion(SourceVersion.RELEASE_6)
1.81 +@SupportedAnnotationTypes({
1.82 + "net.java.html.geo.OnLocation"
1.83 +})
1.84 +public final class GeoProcessor extends AbstractProcessor {
1.85 + private static final Logger LOG = Logger.getLogger(GeoProcessor.class.getName());
1.86 + @Override
1.87 + public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
1.88 + boolean ok = true;
1.89 + for (Element e : roundEnv.getElementsAnnotatedWith(OnLocation.class)) {
1.90 + if (!processLocation(e)) {
1.91 + ok = false;
1.92 + }
1.93 + }
1.94 + return ok;
1.95 + }
1.96 +
1.97 + private void error(String msg, Element e) {
1.98 + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, e);
1.99 + }
1.100 +
1.101 + private boolean processLocation(Element e) {
1.102 + if (e.getKind() != ElementKind.METHOD) {
1.103 + return false;
1.104 + }
1.105 + ExecutableElement me = (ExecutableElement) e;
1.106 + OnLocation ol = e.getAnnotation(OnLocation.class);
1.107 + if (ol == null) {
1.108 + return true;
1.109 + }
1.110 + if (me.getModifiers().contains(Modifier.PRIVATE)) {
1.111 + error("Method annotated by @OnLocation cannot be private", e);
1.112 + return false;
1.113 + }
1.114 + TypeMirror positionClass = processingEnv.getElementUtils().getTypeElement(Position.class.getName()).asType();
1.115 + final List<? extends VariableElement> params = me.getParameters();
1.116 + if (params.size() < 1 || !params.get(0).asType().equals(positionClass)) {
1.117 + error("Method annotated by @OnLocation first argument must be net.java.html.geo.Position!", e);
1.118 + return false;
1.119 + }
1.120 + String className = ol.className();
1.121 + if (className.isEmpty()) {
1.122 + String n = e.getSimpleName().toString();
1.123 + if (n.isEmpty()) {
1.124 + error("Empty method name", e);
1.125 + return false;
1.126 + }
1.127 + final String firstLetter = n.substring(0, 1).toUpperCase(Locale.ENGLISH);
1.128 + className = firstLetter + n.substring(1) + "Handle";
1.129 + }
1.130 + TypeElement te = (TypeElement)e.getEnclosingElement();
1.131 + PackageElement pe = (PackageElement) te.getEnclosingElement();
1.132 + final String pkg = pe.getQualifiedName().toString();
1.133 + final String fqn = pkg + "." + className;
1.134 + final boolean isStatic = me.getModifiers().contains(Modifier.STATIC);
1.135 + String sep;
1.136 + try {
1.137 + JavaFileObject fo = processingEnv.getFiler().createSourceFile(fqn, e);
1.138 + Writer w = fo.openWriter();
1.139 + w.append("package ").append(pkg).append(";\n");
1.140 + w.append("class ").append(className).append(" extends net.java.html.geo.Position.Handle {\n");
1.141 + if (!isStatic) {
1.142 + w.append(" private final ").append(te.getSimpleName()).append(" $i;\n");
1.143 + }
1.144 + for (int i = 1; i < params.size(); i++) {
1.145 + final VariableElement p = params.get(i);
1.146 + w.append(" private final ").append(p.asType().toString()).append(" ").append(p.getSimpleName()).append(";\n");
1.147 + }
1.148 + w.append(" private ").append(className).append("(boolean oneTime");
1.149 + w.append(", ").append(te.getSimpleName()).append(" i");
1.150 + for (int i = 1; i < params.size(); i++) {
1.151 + final VariableElement p = params.get(i);
1.152 + w.append(", ").append(p.asType().toString()).append(" ").append(p.getSimpleName());
1.153 + }
1.154 + w.append(") {\n super(oneTime);\n");
1.155 + if (!isStatic) {
1.156 + w.append(" this.$i = i;\n");
1.157 + }
1.158 + for (int i = 1; i < params.size(); i++) {
1.159 + final VariableElement p = params.get(i);
1.160 + w.append(" this.").append(p.getSimpleName()).append(" = ").append(p.getSimpleName()).append(";\n");
1.161 + }
1.162 + w.append("}\n");
1.163 + w.append(" static net.java.html.geo.Position.Handle createQuery(");
1.164 + String inst;
1.165 + if (!isStatic) {
1.166 + w.append(te.getSimpleName()).append(" instance");
1.167 + inst = "instance";
1.168 + sep = ", ";
1.169 + } else {
1.170 + inst = "null";
1.171 + sep = "";
1.172 + }
1.173 + for (int i = 1; i < params.size(); i++) {
1.174 + final VariableElement p = params.get(i);
1.175 + w.append(sep).append(p.asType().toString()).append(" ").append(p.getSimpleName());
1.176 + sep = ", ";
1.177 + }
1.178 + w.append(") { return new ").append(className).append("(true, ").append(inst);
1.179 + for (int i = 1; i < params.size(); i++) {
1.180 + final VariableElement p = params.get(i);
1.181 + w.append(", ").append(p.getSimpleName());
1.182 + }
1.183 + w.append("); }\n");
1.184 + w.append(" static net.java.html.geo.Position.Handle createWatch(");
1.185 + if (!isStatic) {
1.186 + w.append(te.getSimpleName()).append(" instance");
1.187 + sep = ", ";
1.188 + } else {
1.189 + sep = "";
1.190 + }
1.191 + for (int i = 1; i < params.size(); i++) {
1.192 + final VariableElement p = params.get(i);
1.193 + w.append(sep).append(p.asType().toString()).append(" ").append(p.getSimpleName());
1.194 + }
1.195 + w.append(") { return new ").append(className).append("(false, ").append(inst);
1.196 + for (int i = 1; i < params.size(); i++) {
1.197 + final VariableElement p = params.get(i);
1.198 + w.append(", ").append(p.getSimpleName());
1.199 + }
1.200 + w.append("); }\n");
1.201 + w.append(" @Override protected void onError(Exception t) throws Throwable {\n");
1.202 + if (ol.onError().isEmpty()) {
1.203 + w.append(" t.printStackTrace();");
1.204 + } else {
1.205 + if (!findOnError(me, te, ol.onError(), isStatic)) {
1.206 + return false;
1.207 + }
1.208 + if (isStatic) {
1.209 + w.append(" ").append(te.getSimpleName()).append(".");
1.210 + } else {
1.211 + w.append(" $i.");
1.212 + }
1.213 + w.append(ol.onError()).append("(t");
1.214 + for (int i = 1; i < params.size(); i++) {
1.215 + final VariableElement p = params.get(i);
1.216 + w.append(", ").append(p.getSimpleName());
1.217 + }
1.218 + w.append(");\n");
1.219 + }
1.220 + w.append(" }\n");
1.221 + w.append(" @Override protected void onLocation(net.java.html.geo.Position p) throws Throwable {\n");
1.222 + if (isStatic) {
1.223 + w.append(" ").append(te.getSimpleName()).append(".");
1.224 + } else {
1.225 + w.append(" $i.");
1.226 + }
1.227 + w.append(me.getSimpleName()).append("(p");
1.228 + for (int i = 1; i < params.size(); i++) {
1.229 + final VariableElement p = params.get(i);
1.230 + w.append(", ").append(p.getSimpleName());
1.231 + }
1.232 + w.append(");\n");
1.233 + w.append(" }\n");
1.234 + w.append("}\n");
1.235 + w.close();
1.236 + } catch (IOException ex) {
1.237 + Logger.getLogger(GeoProcessor.class.getName()).log(Level.SEVERE, null, ex);
1.238 + error("Can't write handler class: " + ex.getMessage(), e);
1.239 + return false;
1.240 + }
1.241 +
1.242 + return true;
1.243 + }
1.244 +
1.245 + private boolean findOnError(ExecutableElement errElem, TypeElement te, String name, boolean onlyStatic) {
1.246 + String err = null;
1.247 + METHODS: for (Element e : te.getEnclosedElements()) {
1.248 + if (e.getKind() != ElementKind.METHOD) {
1.249 + continue;
1.250 + }
1.251 + if (!e.getSimpleName().contentEquals(name)) {
1.252 + continue;
1.253 + }
1.254 + if (onlyStatic && !e.getModifiers().contains(Modifier.STATIC)) {
1.255 + errElem = (ExecutableElement) e;
1.256 + err = "Would have to be static";
1.257 + continue;
1.258 + }
1.259 + ExecutableElement ee = (ExecutableElement) e;
1.260 + TypeMirror excType = processingEnv.getElementUtils().getTypeElement(Exception.class.getName()).asType();
1.261 + final List<? extends VariableElement> params = ee.getParameters();
1.262 + if (params.size() < 1 ||
1.263 + !processingEnv.getTypeUtils().isAssignable(excType, ee.getParameters().get(0).asType())
1.264 + ) {
1.265 + errElem = (ExecutableElement) e;
1.266 + err = "Error method first argument needs to be Exception";
1.267 + continue;
1.268 + }
1.269 + final List<? extends Element> origParams = errElem.getParameters();
1.270 + if (params.size() != origParams.size()) {
1.271 + errElem = (ExecutableElement) e;
1.272 + err = "Error method must have the same parameters as @OnLocation one";
1.273 + continue;
1.274 + }
1.275 + for (int i = 1; i < origParams.size(); i++) {
1.276 + final TypeMirror t1 = params.get(i).asType();
1.277 + final TypeMirror t2 = origParams.get(i).asType();
1.278 + if (!processingEnv.getTypeUtils().isSameType(t1, t2)) {
1.279 + errElem = (ExecutableElement) e;
1.280 + err = "Error method must have the same parameters as @OnLocation one";
1.281 + continue METHODS;
1.282 + }
1.283 + }
1.284 + return true;
1.285 + }
1.286 + if (err == null) {
1.287 + err = "Cannot find " + name + "(Exception) method in this class";
1.288 + }
1.289 + error(err, errElem);
1.290 + return false;
1.291 + }
1.292 +}