# HG changeset patch
# Parent 0606c1414754a89f700a41dc53859d2bd520236b
Bug #183794 comment #16: hints to convert layer entries to annotations.
diff --git a/apisupport.project/src/org/netbeans/modules/apisupport/project/layers/LayerNode.java b/apisupport.project/src/org/netbeans/modules/apisupport/project/layers/LayerNode.java
--- a/apisupport.project/src/org/netbeans/modules/apisupport/project/layers/LayerNode.java
+++ b/apisupport.project/src/org/netbeans/modules/apisupport/project/layers/LayerNode.java
@@ -79,6 +79,7 @@
import org.openide.util.RequestProcessor;
import org.openide.util.actions.SystemAction;
import org.openide.util.lookup.Lookups;
+import org.openide.util.lookup.ProxyLookup;
import org.openide.xml.XMLUtil;
/**
@@ -96,7 +97,7 @@
}
private LayerNode(Node delegate, LayerHandle handle, boolean specialDisplayName) {
- super(delegate, new LayerChildren(handle));
+ super(delegate, new LayerChildren(handle), new ProxyLookup(delegate.getLookup(), Lookups.singleton(handle)));
this.specialDisplayName = specialDisplayName;
}
diff --git a/apisupport.refactoring/nbproject/project.xml b/apisupport.refactoring/nbproject/project.xml
--- a/apisupport.refactoring/nbproject/project.xml
+++ b/apisupport.refactoring/nbproject/project.xml
@@ -50,6 +50,15 @@
org.netbeans.modules.apisupport.refactoring
+ org.netbeans.api.annotations.common
+
+
+
+ 1
+ 1.6
+
+
+
org.netbeans.api.java
@@ -85,6 +94,24 @@
+ org.netbeans.modules.editor.errorstripe.api
+
+
+
+ 1
+ 2.14
+
+
+
+ org.netbeans.modules.editor.mimelookup
+
+
+
+ 1
+ 1.20
+
+
+
org.netbeans.modules.java.project
@@ -137,6 +164,15 @@
+ org.netbeans.spi.editor.hints
+
+
+
+ 0
+ 1.15
+
+
+
org.openide.filesystems
diff --git a/apisupport.refactoring/src/org/netbeans/modules/apisupport/hints/ActionRegistrationHinter.java b/apisupport.refactoring/src/org/netbeans/modules/apisupport/hints/ActionRegistrationHinter.java
new file mode 100644
--- /dev/null
+++ b/apisupport.refactoring/src/org/netbeans/modules/apisupport/hints/ActionRegistrationHinter.java
@@ -0,0 +1,84 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2010 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.apisupport.hints;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import org.netbeans.api.java.source.Task;
+import org.netbeans.api.java.source.WorkingCopy;
+import org.netbeans.spi.editor.hints.ChangeInfo;
+import org.netbeans.spi.editor.hints.Fix;
+import org.netbeans.spi.editor.hints.Severity;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ * Transformations for #183794: {@link org.openide.awt.ActionRegistration}
+ */
+@ServiceProvider(service=Hinter.class)
+public class ActionRegistrationHinter implements Hinter {
+
+ public @Override void process(final Context ctx) throws Exception {
+ if ("method:org.openide.awt.Actions.alwaysEnabled".equals(ctx.instanceFile().getAttribute("literal:instanceCreate"))) {
+ ctx.addHint(Severity.WARNING, /*XXX I18N*/"Use of layer entry where annotation is available", new Fix() {
+ public @Override String getText() {
+ return "Convert registration to annotation";
+ }
+ public @Override ChangeInfo implement() throws Exception {
+ ctx.runModificationTask(new Task() {
+ public @Override void run(WorkingCopy wc) throws Exception {
+ Element decl = ctx.findDeclaration(wc, ctx.instanceFile().getAttribute("literal:delegate"));
+ if (decl == null) {
+ return;
+ }
+ // XXX add @ActionRegistration, @ActionID, @ActionReference
+ }
+ });
+ return null;
+ }
+ });
+ }
+ }
+
+}
diff --git a/apisupport.refactoring/src/org/netbeans/modules/apisupport/hints/ConvertToAnnotationHint.java b/apisupport.refactoring/src/org/netbeans/modules/apisupport/hints/ConvertToAnnotationHint.java
new file mode 100644
--- /dev/null
+++ b/apisupport.refactoring/src/org/netbeans/modules/apisupport/hints/ConvertToAnnotationHint.java
@@ -0,0 +1,210 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2010 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.apisupport.hints;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.swing.text.Document;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import org.netbeans.api.editor.mimelookup.MimeRegistration;
+import org.netbeans.api.project.FileOwnerQuery;
+import org.netbeans.api.project.Project;
+import org.netbeans.modules.apisupport.project.api.LayerHandle;
+import org.netbeans.modules.apisupport.project.spi.NbModuleProvider;
+import org.netbeans.spi.editor.errorstripe.UpToDateStatus;
+import org.netbeans.spi.editor.errorstripe.UpToDateStatusProvider;
+import org.netbeans.spi.editor.errorstripe.UpToDateStatusProviderFactory;
+import org.netbeans.spi.editor.hints.ErrorDescription;
+import org.netbeans.spi.editor.hints.HintsController;
+import org.openide.filesystems.FileChangeAdapter;
+import org.openide.filesystems.FileChangeListener;
+import org.openide.filesystems.FileEvent;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileSystem;
+import org.openide.filesystems.FileUtil;
+import org.openide.loaders.DataObject;
+import org.openide.loaders.DataObjectNotFoundException;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.NbCollections;
+import org.openide.util.RequestProcessor;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.DefaultHandler2;
+
+@MimeRegistration(mimeType="text/xml", service=UpToDateStatusProviderFactory.class)
+public class ConvertToAnnotationHint implements UpToDateStatusProviderFactory {
+
+ private static final RequestProcessor RP = new RequestProcessor(ConvertToAnnotationHint.class);
+
+ public @Override UpToDateStatusProvider createUpToDateStatusProvider(Document doc) {
+ Object sdp = doc.getProperty(Document.StreamDescriptionProperty); // avoid dep on NbEditorUtilities.getFileObject if possible
+ DataObject xml;
+ if (sdp instanceof DataObject) {
+ xml = (DataObject) sdp;
+ } else if (sdp instanceof FileObject) {
+ try {
+ xml = DataObject.find((FileObject) sdp);
+ } catch (DataObjectNotFoundException x) {
+ Exceptions.printStackTrace(x);
+ return null;
+ }
+ } else {
+ return null;
+ }
+ LayerHandle handle = xml.getNodeDelegate().getLookup().lookup(LayerHandle.class);
+ if (handle == null) {
+ return null;
+ }
+ Project project = FileOwnerQuery.getOwner(xml.getPrimaryFile());
+ if (project == null || project.getLookup().lookup(NbModuleProvider.class) == null) {
+ return null;
+ }
+ return new Prov(doc, xml, handle);
+ }
+
+ private static class Prov extends UpToDateStatusProvider implements Runnable {
+
+ private final Document doc;
+ private final LayerHandle handle;
+ private boolean processed;
+ private final FileChangeListener listener = new FileChangeAdapter() {
+ public @Override void fileChanged(FileEvent fe) {
+ processed = false;
+ RP.post(Prov.this); // XXX use schedule on a Task
+ firePropertyChange(PROP_UP_TO_DATE, null, null);
+ }
+ };
+
+ Prov(Document doc, DataObject xml, LayerHandle handle) {
+ this.doc = doc;
+ this.handle = handle;
+ xml.getPrimaryFile().addFileChangeListener(FileUtil.weakFileChangeListener(listener, xml.getPrimaryFile()));
+ RP.post(this);
+ }
+
+ public @Override UpToDateStatus getUpToDate() {
+ if (processed) {
+ return UpToDateStatus.UP_TO_DATE_OK;
+ }
+ return processed ? UpToDateStatus.UP_TO_DATE_OK : UpToDateStatus.UP_TO_DATE_PROCESSING;
+ }
+
+ public @Override void run() {
+ FileSystem fs = handle.layer(false);
+ if (fs == null) {
+ return;
+ }
+ Set instances = new LinkedHashSet();
+ // Compare AbstractRefactoringPlugin.checkFileObject:
+ FILE: for (FileObject f : NbCollections.iterable(fs.getRoot().getData(true))) {
+ if (!f.hasExt("instance")) {
+ continue; // not supporting *.settings etc. for now
+ }
+ instances.add(f);
+ }
+ List errors = new ArrayList();
+ if (!instances.isEmpty()) {
+ final Map lines = new HashMap();
+ try { // Adapted from OpenLayerFilesAction.openLayerFileAndFind:
+ InputSource in = new InputSource(handle.getLayerFile().getURL().toExternalForm());
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ SAXParser parser = factory.newSAXParser();
+ class Handler extends DefaultHandler2 {
+ private Locator locator;
+ private String path;
+ public @Override void setDocumentLocator(Locator l) {
+ locator = l;
+ }
+ public @Override void startElement(String uri, String localname, String qname, Attributes attr) throws SAXException {
+ if (!qname.matches("file|folder")) { // NOI18N
+ return;
+ }
+ String n = attr.getValue("name"); // NOI18N
+ path = path == null ? n : path + '/' + n;
+ lines.put(path, locator.getLineNumber());
+ }
+ public @Override void endElement(String uri, String localname, String qname) throws SAXException {
+ if (!qname.matches("file|folder")) { // NOI18N
+ return;
+ }
+ int slash = path.lastIndexOf('/');
+ path = slash == -1 ? null : path.substring(0, slash);
+ }
+ }
+ DefaultHandler2 handler = new Handler();
+ parser.getXMLReader().setProperty("http://xml.org/sax/properties/lexical-handler", handler); // NOI18N
+ parser.parse(in, handler);
+ } catch (Exception x) {
+ Exceptions.printStackTrace(x);
+ }
+ for (FileObject instance : instances) {
+ Integer line = lines.get(instance.getPath());
+ if (line == null) {
+ System.err.println("XXX no line for " + instance.getPath());
+ continue;
+ }
+ for (Hinter hinter : Lookup.getDefault().lookupAll(Hinter.class)) {
+ try {
+ hinter.process(new Hinter.Context(doc, handle, instance, line, errors));
+ } catch (Exception x) {
+ Exceptions.printStackTrace(x);
+ }
+ }
+ }
+ }
+ HintsController.setErrors(doc, ConvertToAnnotationHint.class.getName(), errors);
+ processed = true;
+ firePropertyChange(PROP_UP_TO_DATE, null, null);
+ }
+
+ }
+
+}
diff --git a/apisupport.refactoring/src/org/netbeans/modules/apisupport/hints/Hinter.java b/apisupport.refactoring/src/org/netbeans/modules/apisupport/hints/Hinter.java
new file mode 100644
--- /dev/null
+++ b/apisupport.refactoring/src/org/netbeans/modules/apisupport/hints/Hinter.java
@@ -0,0 +1,156 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2010 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.apisupport.hints;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.swing.text.Document;
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.netbeans.api.java.source.ClasspathInfo;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.Task;
+import org.netbeans.api.java.source.WorkingCopy;
+import org.netbeans.modules.apisupport.project.api.LayerHandle;
+import org.netbeans.spi.editor.hints.ErrorDescription;
+import org.netbeans.spi.editor.hints.ErrorDescriptionFactory;
+import org.netbeans.spi.editor.hints.Fix;
+import org.netbeans.spi.editor.hints.Severity;
+import org.openide.filesystems.FileObject;
+
+/**
+ * One category of hint.
+ * Register implementation into global lookup.
+ */
+public interface Hinter {
+
+ /**
+ * Check for hints.
+ * @param ctx context of a single layer entry
+ * @throws Exception in case of problem
+ */
+ void process(Context ctx) throws Exception;
+
+ /**
+ * Context supplied to a {@link Hinter}.
+ */
+ class Context {
+
+ private final Document doc;
+ private final LayerHandle layer;
+ private final FileObject instanceFile;
+ private final int line;
+ private final List super ErrorDescription> errors;
+
+ Context(Document doc, LayerHandle layer, FileObject instanceFile, int line, List super ErrorDescription> errors) {
+ this.doc = doc;
+ this.layer = layer;
+ this.instanceFile = instanceFile;
+ this.line = line;
+ this.errors = errors;
+ }
+
+ /**
+ * Gets the layer entry you may offer hints for.
+ * File attribute names like {@code literal:instanceCreate} may return values like {@code new:pkg.Clazz} or {@code method:pkg.Clazz.factory}.
+ * @return a {@code *.instance} file in the project's layer
+ */
+ public FileObject instanceFile() {
+ return instanceFile;
+ }
+
+ /**
+ * Add a hint.
+ * @param severity whether to treat as a warning, etc.
+ * @param description description of hint
+ * @param fixes any fixes to offer
+ */
+ public void addHint(Severity severity, String description, Fix... fixes) {
+ errors.add(ErrorDescriptionFactory.createErrorDescription(severity, description, Arrays.asList(fixes), doc, line));
+ }
+
+ /**
+ * Runs (and commits) a Java source modification task in the context of the project with this layer.
+ * @param task a task to run
+ * @throws IOException if it could not be run
+ */
+ public void runModificationTask(Task task) throws IOException {
+ JavaSource.create(ClasspathInfo.create(layer.getLayerFile())).runModificationTask(task).commit();
+ }
+
+ /**
+ * Locate the declaration of an object declared as a newvalue or methodvalue attribute.
+ * @param wc as per {@link #runModificationTask}
+ * @param instanceAttribute the result of {@link FileObject#getAttribute} on a {@code literal:*} key
+ * @return the corresponding declaration, or null if not found
+ */
+ public @CheckForNull Element findDeclaration(WorkingCopy wc, @NullAllowed Object instanceAttribute) {
+ if (!(instanceAttribute instanceof String)) {
+ return null;
+ }
+ String attr = (String) instanceAttribute;
+ if (attr.startsWith("new:")) {
+ return wc.getElements().getTypeElement(attr.substring(4).replace('$', '.'));
+ } else if (attr.startsWith("method:")) {
+ int dot = attr.lastIndexOf('.');
+ TypeElement type = wc.getElements().getTypeElement(attr.substring(7, dot).replace('$', '.'));
+ if (type != null) {
+ String meth = attr.substring(dot + 1);
+ for (Element check : type.getEnclosedElements()) {
+ if (check.getKind() == ElementKind.METHOD && check.getSimpleName().contentEquals(meth) && ((ExecutableElement) check).getParameters().isEmpty()) {
+ return check;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ }
+
+}