@@ -, +, @@ Projects/o-n-m-maven/Customizer/Formatting/ @1000 (o.n.m.options.editor: "Formatting") --- a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/ui/customizer/Bundle.properties +++ a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/ui/customizer/Bundle.properties @@ -41,17 +41,14 @@ LBL_Customizer_Title=Project Properties - {0} # Configuration node labels -Projects/org-netbeans-modules-java-j2seproject/Customizer/BuildCategory=Build -Projects/org-netbeans-modules-java-j2seproject/Customizer/WebServiceCategory=Web Services -Projects/org-netbeans-modules-java-j2seproject/Customizer/Application=Application +LBL_Config_BuildCategory=Build +LBL_Config_Application=Application LBL_Config_Libraries=Libraries LBL_Config_Sources=Sources LBL_Config_Build=Compiling LBL_Config_Jar=Packaging LBL_Config_Javadoc=Documenting LBL_Config_Run=Run -LBL_Config_WebServiceClients=Web Service Clients -LBL_Config_WebServices=JAX-RPC Web Services # Panels --- a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/ui/customizer/J2SECompositePanelProvider.java +++ a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/ui/customizer/J2SECompositePanelProvider.java @@ -131,30 +131,63 @@ } + @ProjectCustomizer.CompositeCategoryProvider.Registration( + projectType="org-netbeans-modules-java-j2seproject", + position=100 + ) public static J2SECompositePanelProvider createSources() { return new J2SECompositePanelProvider(SOURCES); } + @ProjectCustomizer.CompositeCategoryProvider.Registration( + projectType="org-netbeans-modules-java-j2seproject", + position=200 + ) public static J2SECompositePanelProvider createLibraries() { return new J2SECompositePanelProvider(LIBRARIES); } + @ProjectCustomizer.CompositeCategoryProvider.Registration( + projectType="org-netbeans-modules-java-j2seproject", + category="BuildCategory", + position=100 + ) public static J2SECompositePanelProvider createBuild() { return new J2SECompositePanelProvider(BUILD); } + @ProjectCustomizer.CompositeCategoryProvider.Registration( + projectType="org-netbeans-modules-java-j2seproject", + category="BuildCategory", + position=200 + ) public static J2SECompositePanelProvider createJar() { return new J2SECompositePanelProvider(JAR); } + @ProjectCustomizer.CompositeCategoryProvider.Registration( + projectType="org-netbeans-modules-java-j2seproject", + category="BuildCategory", + position=300 + ) public static J2SECompositePanelProvider createJavadoc() { return new J2SECompositePanelProvider(JAVADOC); } + @ProjectCustomizer.CompositeCategoryProvider.Registration( + projectType="org-netbeans-modules-java-j2seproject", + position=400 + ) public static J2SECompositePanelProvider createRun() { return new J2SECompositePanelProvider(RUN); } + @ProjectCustomizer.CompositeCategoryProvider.Registration( + projectType="org-netbeans-modules-java-j2seproject", + category="Application", + position=500, + categoryLabel="#LBL_Config_Application" + ) public static J2SECompositePanelProvider createApplication() { return new J2SECompositePanelProvider(APPLICATION); } --- a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/ui/customizer/package-info.java +++ a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/ui/customizer/package-info.java @@ -0,0 +1,48 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * + * 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 2009 Sun Microsystems, Inc. + */ + +@ProjectCustomizer.CompositeCategoryProvider.Registration( + projectType="org-netbeans-modules-java-j2seproject", + category="BuildCategory", + position=300, + categoryLabel="#LBL_Config_BuildCategory" +) +package org.netbeans.modules.java.j2seproject.ui.customizer; + +import org.netbeans.spi.project.ui.support.ProjectCustomizer; --- a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/ui/resources/layer.xml +++ a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/ui/resources/layer.xml @@ -93,45 +93,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --- a/openide.filesystems/src/org/openide/filesystems/annotations/LayerBuilder.java +++ a/openide.filesystems/src/org/openide/filesystems/annotations/LayerBuilder.java @@ -90,7 +90,23 @@ * @return a file builder */ public File file(String path) { - File f = new File(path); + File f = new File(path, false); + unwrittenFiles.add(f); + return f; + } + + /** + * Adds a folder to the layer. + * You need to {@link File#write} it in order to finalize the effect. + *

Normally just using {@link #file} suffices, since parent folders are + * created as needed, but you may use this method if you wish to create a folder + * (possibly with some attributes) without necessarily creating any children. + * @param path the full path to the desired folder in resource format, e.g. {@code "Menu/File"} + * @return a file builder + * @since XXX + */ + public File folder(String path) { + File f = new File(path, true); unwrittenFiles.add(f); return f; } @@ -209,12 +225,14 @@ public final class File { private final String path; + private final boolean folder; private final Map attrs = new LinkedHashMap(); private String contents; private String url; - File(String path) { + File(String path, boolean folder) { this.path = path; + this.folder = folder; } /** @@ -231,7 +249,7 @@ * @return this builder */ public File contents(String contents) { - if (this.contents != null || url != null || contents == null) { + if (this.contents != null || url != null || contents == null || folder) { throw new IllegalArgumentException(); } this.contents = contents; @@ -245,7 +263,7 @@ * @return this builder */ public File url(String url) { - if (contents != null || this.url != null || url == null) { + if (contents != null || this.url != null || url == null || folder) { throw new IllegalArgumentException(); } this.url = url; @@ -524,9 +542,9 @@ } /** - * Writes the file to the layer. + * Writes the file or folder to the layer. * Any intervening parent folders are created automatically. - * If the file already exists, the old copy is replaced. + * If the file already exists, the old copy is replaced (not true in case of a folder). * @return the originating layer builder, in case you want to add another file */ public LayerBuilder write() { @@ -547,10 +565,16 @@ } String piece = pieces[pieces.length - 1]; org.w3c.dom.Element file = find(e,piece); - if (file != null) { - e.removeChild(file); + if (folder) { + if (file == null) { + file = (org.w3c.dom.Element) e.appendChild(doc.createElement("folder")); + } + } else { + if (file != null) { + e.removeChild(file); + } + file = (org.w3c.dom.Element) e.appendChild(doc.createElement("file")); } - file = (org.w3c.dom.Element) e.appendChild(doc.createElement("file")); file.setAttribute("name", piece); for (Map.Entry entry : attrs.entrySet()) { org.w3c.dom.Element attr = (org.w3c.dom.Element) file.appendChild(doc.createElement("attr")); --- a/openide.filesystems/test/unit/src/org/openide/filesystems/annotations/LayerBuilderTest.java +++ a/openide.filesystems/test/unit/src/org/openide/filesystems/annotations/LayerBuilderTest.java @@ -135,4 +135,14 @@ "", dump()); } + public void testFolders() throws Exception { + b.file("x/y").write(); + b.folder("x/z").stringvalue("a", "v").write(); + b.folder("x").write(); + assertEquals("" + + "" + + "" + + "", dump()); + } + } --- a/projectuiapi/src/org/netbeans/modules/project/uiapi/CompositeCategoryProviderAnnotationProcessor.java +++ a/projectuiapi/src/org/netbeans/modules/project/uiapi/CompositeCategoryProviderAnnotationProcessor.java @@ -0,0 +1,109 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * + * 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 2009 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.project.uiapi; + +import java.util.Set; +import javax.annotation.processing.Processor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.TypeElement; +import org.netbeans.spi.project.ui.support.ProjectCustomizer.CompositeCategoryProvider; +import org.netbeans.spi.project.ui.support.ProjectCustomizer.CompositeCategoryProvider.Registration; +import org.netbeans.spi.project.ui.support.ProjectCustomizer.CompositeCategoryProvider.Registrations; +import org.openide.filesystems.annotations.LayerBuilder.File; +import org.openide.filesystems.annotations.LayerGeneratingProcessor; +import org.openide.filesystems.annotations.LayerGenerationException; +import org.openide.util.lookup.ServiceProvider; + +@ServiceProvider(service=Processor.class) +@SupportedSourceVersion(SourceVersion.RELEASE_6) +@SupportedAnnotationTypes({ + "org.netbeans.spi.project.ui.support.ProjectCustomizer.CompositeCategoryProvider.Registration", + "org.netbeans.spi.project.ui.support.ProjectCustomizer.CompositeCategoryProvider.Registrations" +}) +public class CompositeCategoryProviderAnnotationProcessor extends LayerGeneratingProcessor { + + protected boolean handleProcess(Set annotations, RoundEnvironment roundEnv) throws LayerGenerationException { + if (roundEnv.processingOver()) { + return false; + } + for (Element e : roundEnv.getElementsAnnotatedWith(Registration.class)) { + handle(e, e.getAnnotation(Registration.class)); + } + for (Element e : roundEnv.getElementsAnnotatedWith(Registrations.class)) { + for (Registration r : e.getAnnotation(Registrations.class).value()) { + handle(e, r); + } + } + return true; + } + + private void handle(Element e, Registration r) throws LayerGenerationException { + String path = "Projects/" + r.projectType() + "/Customizer"; + if (r.category().length() > 0) { + path += "/" + r.category(); + } + boolean addsFolder = r.categoryLabel().length() > 0; + if (addsFolder) { + handleFolder(path, e, r); + } + if (e.getKind() == ElementKind.PACKAGE) { + if (!addsFolder) { + throw new LayerGenerationException("Must specify categoryLabel", e); + } + } else { + File f = layer(e).instanceFile(path, addsFolder ? "Self" : null, CompositeCategoryProvider.class); + f.position(addsFolder ? 0 : r.position()); + f.write(); + } + } + + private void handleFolder(String path, Element e, Registration r) throws LayerGenerationException { + if (r.category().length() == 0) { + throw new LayerGenerationException("Must specify category", e); + } + layer(e).folder(path).bundlevalue("displayName", r.categoryLabel()).position(r.position()).write(); + } + +} --- a/projectuiapi/src/org/netbeans/spi/project/ui/support/ProjectCustomizer.java +++ a/projectuiapi/src/org/netbeans/spi/project/ui/support/ProjectCustomizer.java @@ -45,6 +45,10 @@ import java.awt.Image; import java.awt.event.ActionListener; import java.io.IOException; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -288,11 +292,12 @@ /** * Interface for creation of Customizer categories and their respective UI panels. - * Implementations are to be registered in System FileSystem via module layers. Used by the - * {@link ProjectCustomizer#createCustomizerDialog(String,Lookup,String,ActionListener,HelpCtx)} - * The panel/category created by the provider can get notified that the customizer got - * closed by setting an ActionListener to + * Used by {@link ProjectCustomizer#createCustomizerDialog(String,Lookup,String,ActionListener,HelpCtx)}. + *

The panel/category created by the provider can get notified that the customizer got + * closed by setting an ActionListener to * {@link ProjectCustomizer.Category#setOkButtonListener}. + *

Implementations can be registered using {@link Registration}. + * Otherwise they can be manually registered in a tree structure in the system filesystem. * UI Component can be defined for category folder that is represented as node with subnodes in the category * tree of project customizer. The file that defines the instance class in layer for such category * must be named {@code Self}. Such a provider will not have the {@link #createCategory} method called @@ -322,6 +327,63 @@ * for the project type you want to integrate your panel into. */ JComponent createComponent (Category category, Lookup context ); + + /** + * Used to register customizer panels. + * There are three ways this annotation can be used: + *

    + *
  1. Register a "leaf" panel with no children. + * {@link #category} can be omitted for a top-level panel; + * if specified, the panel is placed in the named subcategory. + * {@link #categoryLabel} should not be specified. + * The annotation must be placed on a class or factory method implementing {@link CompositeCategoryProvider}. + *
  2. Register a category folder with no panel. + * {@link #category} must be specified; the last path component is the + * folder being defined, and any previous components are parent folders. + * {@link #categoryLabel} must be specified. + * The annotation must be placed on some package declaration (in {@code package-info.java}). + *
  3. Register a category folder also with its own panel (i.e. {@code Self}). + * {@link #category} and {@link #categoryLabel} must be specified as for #2, + * but the annotation must be on a provider implementation as for #1. + *
+ * To represent hierarchies of panels, the {@link #category} of a #1 can + * match the {@link #category} of a #2 or #3, and the {@link #category} of a #2 or #3 + * preceding the last {@code /} can match the {@link #category} of another #2 or #3. + *

Multiple registrations may be made in one place using {@link Registrations}. + * @since XXX + */ + @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PACKAGE}) + @Retention(RetentionPolicy.SOURCE) + @interface Registration { + /** + * Project type to associate with, such as {@code org-netbeans-modules-java-j2seproject}. + * The {@code folderPath} passed to {@link ProjectCustomizer#createCustomizerDialog(String,Lookup,String,ActionListener,HelpCtx)} + * should be {@code Projects//Customizer}. + */ + String projectType(); + /** + * Category folder (perhaps multiple components separated by {@code /}) + * in which to place this panel or which is the name of this panel folder. + */ + String category() default ""; + /** + * Display name when defining a category folder. + * Can use {@code pkg.of.Bundle#key_name} syntax. + */ + String categoryLabel() default ""; + /** + * Position of this panel or subfolder within its folder. + */ + int position() default Integer.MAX_VALUE; + } + /** + * Used in case multiple registrations are needed in one place. + */ + @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PACKAGE}) + @Retention(RetentionPolicy.SOURCE) + @interface Registrations { + Registration[] value(); + } } /** Describes category of properties to be customized by given component --- a/projectuiapi/test/unit/src/org/netbeans/spi/project/ui/support/ProjectCustomizerTest.java +++ a/projectuiapi/test/unit/src/org/netbeans/spi/project/ui/support/ProjectCustomizerTest.java @@ -57,7 +57,6 @@ import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.loaders.DataFolder; -import org.openide.loaders.InstanceDataObject; import org.openide.util.HelpCtx; import org.openide.util.Lookup; @@ -107,16 +106,6 @@ // - Three | three // + Category #2 | // - Four | four - InstanceDataObject.create(DataFolder.findFolder(customizerFO), null, TestCCP1.class).getPrimaryFile().setAttribute("position", 100); - FileObject catFO = customizerFO.createFolder("Category1"); - catFO.setAttribute("displayName", "Category #1"); - catFO.setAttribute("position", 200); - InstanceDataObject.create(DataFolder.findFolder(catFO), "Self", TestCCP2.class); - InstanceDataObject.create(DataFolder.findFolder(catFO), null, TestCCP3.class); - catFO = customizerFO.createFolder("Category2"); - catFO.setAttribute("displayName", "Category #2"); - catFO.setAttribute("position", 300); - InstanceDataObject.create(DataFolder.findFolder(catFO), null, TestCCP4.class); DelegateCategoryProvider dcp = new DelegateCategoryProvider(DataFolder.findFolder(customizerFO), null); Category categories[] = dcp.readCategories(DataFolder.findFolder(customizerFO)); assertNotNull(categories); @@ -156,6 +145,9 @@ return c; } } + @CompositeCategoryProvider.Registration( + projectType="test", + position=100) public static class TestCCP1 extends TestCCP { public TestCCP1() { super("one"); @@ -164,6 +156,11 @@ return Category.create("one", "One", null); } } + @CompositeCategoryProvider.Registration( + projectType="test", + category="Category1", + categoryLabel="Category #1", + position=200) public static class TestCCP2 extends TestCCP { public TestCCP2() { super("two"); @@ -172,6 +169,10 @@ throw new AssertionError("Self"); } } + @CompositeCategoryProvider.Registration( + projectType="test", + category="Category1", + position=100) public static class TestCCP3 extends TestCCP { public TestCCP3() { super("three"); @@ -180,6 +181,10 @@ return Category.create("three", "Three", null); } } + @CompositeCategoryProvider.Registration( + projectType="test", + category="Category2", + position=100) public static class TestCCP4 extends TestCCP { public TestCCP4() { super("four"); --- a/projectuiapi/test/unit/src/org/netbeans/spi/project/ui/support/package-info.java +++ a/projectuiapi/test/unit/src/org/netbeans/spi/project/ui/support/package-info.java @@ -0,0 +1,47 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * + * 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 2009 Sun Microsystems, Inc. + */ + +@CompositeCategoryProvider.Registration( + projectType="test", + category="Category2", + categoryLabel="Category #2", + position=300) +package org.netbeans.spi.project.ui.support; + +import org.netbeans.spi.project.ui.support.ProjectCustomizer.CompositeCategoryProvider;