diff --git a/java.freeform/apichanges.xml b/java.freeform/apichanges.xml --- a/java.freeform/apichanges.xml +++ b/java.freeform/apichanges.xml @@ -48,6 +48,18 @@ + + Support for annotation processing + + + + + + Added complete support for annotation processing into freeform-project-java/3 schema which now supports all metadata for + AnnotationProcessingQuery. + + + Added freeform-project-java/3 schema to support annotation processors and source levels 1.6 and 1.7 diff --git a/java.freeform/manifest.mf b/java.freeform/manifest.mf --- a/java.freeform/manifest.mf +++ b/java.freeform/manifest.mf @@ -1,6 +1,6 @@ Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.modules.java.freeform/1 -OpenIDE-Module-Specification-Version: 1.20 +OpenIDE-Module-Specification-Version: 1.21 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/java/freeform/Bundle.properties OpenIDE-Module-Layer: org/netbeans/modules/java/freeform/resources/layer.xml AutoUpdate-Show-In-Client: false diff --git a/java.freeform/src/org/netbeans/modules/java/freeform/AnnotationProcessingQueryImpl.java b/java.freeform/src/org/netbeans/modules/java/freeform/AnnotationProcessingQueryImpl.java --- a/java.freeform/src/org/netbeans/modules/java/freeform/AnnotationProcessingQueryImpl.java +++ b/java.freeform/src/org/netbeans/modules/java/freeform/AnnotationProcessingQueryImpl.java @@ -39,42 +39,273 @@ package org.netbeans.modules.java.freeform; +import java.net.MalformedURLException; import java.net.URL; -import java.util.Collections; +import java.util.ArrayList; +import java.util.Collection; import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; import javax.swing.event.ChangeListener; +import org.netbeans.api.java.queries.AnnotationProcessingQuery; import org.netbeans.api.java.queries.AnnotationProcessingQuery.Result; import org.netbeans.api.java.queries.AnnotationProcessingQuery.Trigger; +import org.netbeans.api.project.ProjectManager; import org.netbeans.spi.java.queries.AnnotationProcessingQueryImplementation; +import org.netbeans.spi.project.AuxiliaryConfiguration; +import org.netbeans.spi.project.support.ant.AntProjectEvent; +import org.netbeans.spi.project.support.ant.AntProjectHelper; +import org.netbeans.spi.project.support.ant.AntProjectListener; +import org.netbeans.spi.project.support.ant.PropertyEvaluator; import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.util.ChangeSupport; +import org.openide.util.Exceptions; +import org.openide.util.Mutex; +import org.openide.xml.XMLUtil; +import org.w3c.dom.Element; /** - * Currently there is no special control over AP other than {@code }. + * Freeform implementation of AnnotationProcessingQueryImplementation */ -class AnnotationProcessingQueryImpl implements AnnotationProcessingQueryImplementation { +class AnnotationProcessingQueryImpl implements AnnotationProcessingQueryImplementation, AntProjectListener { - public AnnotationProcessingQueryImpl() {} + static final String EL_ANNOTATION_PROCESSING = "annotation-processing"; //NOI18N + static final String EL_PROCESSOR_PATH = "processor-path"; //NOI18N + private static final String EL_SCAN_TRIGGER = "scan-trigger"; //NOI18N + private static final String EL_EDITOR_TRIGGER = "editor-trigger"; //NOI18N + private static final String EL_PROCESSOR = "processor"; //NOI18N + private static final String EL_PROCESSOR_OPTION = "processor-option"; //NOI18N + private static final String EL_SOURCE_OUTPUT ="source-output"; //NOI18N - public @Override Result getAnnotationProcessingOptions(FileObject file) { - return new Result() { - public @Override Set annotationProcessingEnabled() { - return EnumSet.allOf(Trigger.class); + private final Object LCK = new Object(); + + private final AntProjectHelper helper; + private final PropertyEvaluator eval; + private final AuxiliaryConfiguration aux; + private Map results; + + @SuppressWarnings("LeakingThisInConstructor") + public AnnotationProcessingQueryImpl(AntProjectHelper helper, PropertyEvaluator eval, final AuxiliaryConfiguration aux) { + assert helper != null; + assert eval != null; + assert aux != null; + this.helper = helper; + this.eval = eval; + this.aux = aux; + this.helper.addAntProjectListener(this); + } + + public @Override Result getAnnotationProcessingOptions(final FileObject file) { + assert file != null; + return ProjectManager.mutex().readAccess(new Mutex.Action() { + public Result run() { + synchronized (LCK) { + init(true); + assert results != null; + for (Map.Entry entry : results.entrySet()) { + FileObject fo = entry.getKey(); + if (fo.equals(file) || FileUtil.isParentOf(fo, file)) { + return entry.getValue(); + } + } + return null; + } } - public @Override Iterable annotationProcessorsToRun() { - return null; + }); + } + + @Override + public void configurationXmlChanged(AntProjectEvent ev) { + init (false); + } + + @Override + public void propertiesChanged(AntProjectEvent ev) { + //pass + } + + private void init(final boolean force) { + synchronized (LCK) { + if (results == null) { + if (force) { + results = new HashMap(); + } else { + return; + } } - public @Override URL sourceOutputDirectory() { - return null; + final Map added = new HashMap(); + final Map retained = new HashMap(); + + final Element java = aux.getConfigurationFragment(JavaProjectNature.EL_JAVA, JavaProjectNature.NS_JAVA_3, true); + if (java != null) { + for (Element compilationUnit : XMLUtil.findSubElements(java)) { + assert compilationUnit.getLocalName().equals("compilation-unit") : compilationUnit; + final List packageRoots = Classpaths.findPackageRoots(helper, eval, compilationUnit); + for (FileObject source : packageRoots) { + final R r = results.remove(source); + if (r == null) { + // Create a new result + added.put(source, new R(compilationUnit)); + } else { + //Recalculate and fire + retained.put(source,r); + r.update(compilationUnit); + } + } + } } - public @Override Map processorOptions() { - return Collections.emptyMap(); + + //Invalidate results for remvoed roots + for (R r : results.values()) { + r.update(null); } - public @Override void addChangeListener(ChangeListener l) {} - public @Override void removeChangeListener(ChangeListener l) {} + results.putAll(added); + results.putAll(retained); + } + } - }; + + + private class R implements AnnotationProcessingQuery.Result { + + private final ChangeSupport changeSupport = new ChangeSupport(this); + private volatile Element ap; + private volatile Set triggerCache; + private volatile Map optionsCache; + private volatile Collection processorsCache; + private volatile List sourceOutputCache; + + private R(final Element cu) { + assert cu != null; + this.ap = findAP(cu); + } + + public @Override Set annotationProcessingEnabled() { + Set result = triggerCache; + if (result != null) { + return result; + } + result = EnumSet.noneOf(Trigger.class); + if (ap != null) { + for (Element e : XMLUtil.findSubElements(ap)) { + if (e.getLocalName().equals(EL_SCAN_TRIGGER)) { + result.add(Trigger.ON_SCAN); + } else if (e.getLocalName().equals(EL_EDITOR_TRIGGER)) { + result.add(Trigger.IN_EDITOR); + } + } + } + synchronized (LCK) { + if (triggerCache == null) { + triggerCache = result; + } + } + return result; + } + + public @Override Iterable annotationProcessorsToRun() { + Collection result = processorsCache; + if (result != null) { + return result.isEmpty() ? null : result; + } + result = new ArrayList(); + if (ap != null) { + for (Element e : XMLUtil.findSubElements(ap)) { + if (e.getLocalName().equals(EL_PROCESSOR)) { + result.add(eval.evaluate(XMLUtil.findText(e))); + } + } + } + synchronized (LCK) { + if (processorsCache == null) { + processorsCache = result; + } + } + return result.isEmpty() ? null : result; + } + + + public @Override URL sourceOutputDirectory() { + List result = sourceOutputCache; + if (result != null) { + return result.get(0); + } + result = new ArrayList(1); + if (ap != null) { + for (Element e : XMLUtil.findSubElements(ap)) { + if (e.getLocalName().equals(EL_SOURCE_OUTPUT)) { + try { + final String path = eval.evaluate(XMLUtil.findText(e)); + result.add(helper.resolveFile(path).toURI().toURL()); + } catch (MalformedURLException ex) { + Exceptions.printStackTrace(ex); + } + break; + } + } + } + synchronized (LCK) { + if (sourceOutputCache == null) { + sourceOutputCache = result; + } + } + return result.get(0); + } + + public @Override Map processorOptions() { + Map result = optionsCache; + if (result != null) { + return result; + } + result = new HashMap(); + if (ap != null) { + for (Element e : XMLUtil.findSubElements(ap)) { + if (e.getLocalName().equals(EL_PROCESSOR_OPTION)) { + final Element keyElement = XMLUtil.findElement(e, "key", JavaProjectNature.NS_JAVA_3); //NOI18N + final Element valueElement = XMLUtil.findElement(e, "value", JavaProjectNature.NS_JAVA_3); //NOI18N + if (keyElement == null || valueElement == null) { + continue; + } + final String key = XMLUtil.findText(keyElement); + final String value = XMLUtil.findText(valueElement); + result.put(key, eval.evaluate(value)); + } + } + } + synchronized (LCK) { + if (optionsCache == null) { + optionsCache = result; + } + } + return result; + } + + public @Override void addChangeListener(ChangeListener l) { + changeSupport.addChangeListener(l); + } + + public @Override void removeChangeListener(ChangeListener l) { + changeSupport.removeChangeListener(l); + } + + private void update (final Element cu) { + synchronized(LCK) { + triggerCache = null; + optionsCache = null; + processorsCache = null; + sourceOutputCache = null; + ap = findAP(cu); + } + this.changeSupport.fireChange(); + } + + private Element findAP(final Element cu) { + return cu == null ? null : XMLUtil.findElement(cu, AnnotationProcessingQueryImpl.EL_ANNOTATION_PROCESSING, JavaProjectNature.NS_JAVA_3); + } + } } diff --git a/java.freeform/src/org/netbeans/modules/java/freeform/Classpaths.java b/java.freeform/src/org/netbeans/modules/java/freeform/Classpaths.java --- a/java.freeform/src/org/netbeans/modules/java/freeform/Classpaths.java +++ b/java.freeform/src/org/netbeans/modules/java/freeform/Classpaths.java @@ -485,11 +485,13 @@ } return urls; } - + private List createProcessorClasspath(Element compilationUnitEl) { - for (Element e : XMLUtil.findSubElements(compilationUnitEl)) { - if (e.getLocalName().equals("classpath") && e.getAttribute("mode").equals("processor")) { // NOI18N - return createClasspath(e); + final Element ap = XMLUtil.findElement(compilationUnitEl, AnnotationProcessingQueryImpl.EL_ANNOTATION_PROCESSING, JavaProjectNature.NS_JAVA_3); + if (ap != null) { + final Element path = XMLUtil.findElement(ap, AnnotationProcessingQueryImpl.EL_PROCESSOR_PATH, JavaProjectNature.NS_JAVA_3); + if (path != null) { + return createClasspath(path); } } // None specified; assume it is the same as the compile classpath. diff --git a/java.freeform/src/org/netbeans/modules/java/freeform/LookupProviderImpl.java b/java.freeform/src/org/netbeans/modules/java/freeform/LookupProviderImpl.java --- a/java.freeform/src/org/netbeans/modules/java/freeform/LookupProviderImpl.java +++ b/java.freeform/src/org/netbeans/modules/java/freeform/LookupProviderImpl.java @@ -87,7 +87,7 @@ cp, // ClassPathProvider new SourceLevelQueryImpl(projectHelper, projectEvaluator, aux), // SourceLevelQueryImplementation new SourceForBinaryQueryImpl(projectHelper, projectEvaluator, aux), // SourceForBinaryQueryImplementation - new AnnotationProcessingQueryImpl(), + new AnnotationProcessingQueryImpl(projectHelper, projectEvaluator, aux), new OpenHook(project, cp), // ProjectOpenedHook new TestQuery(projectHelper, projectEvaluator, aux), // MultipleRootsUnitTestForSourceQueryImplementation new JavadocQuery(projectHelper, projectEvaluator, aux), // JavadocForBinaryQueryImplementation diff --git a/java.freeform/src/org/netbeans/modules/java/freeform/resources/freeform-project-java-3.xsd b/java.freeform/src/org/netbeans/modules/java/freeform/resources/freeform-project-java-3.xsd --- a/java.freeform/src/org/netbeans/modules/java/freeform/resources/freeform-project-java-3.xsd +++ b/java.freeform/src/org/netbeans/modules/java/freeform/resources/freeform-project-java-3.xsd @@ -74,7 +74,6 @@ - @@ -95,6 +94,18 @@ + + + + + + + + + + + + @@ -105,4 +116,13 @@ + + + + + + + + +