--- a/apisupport.ant/src/org/netbeans/modules/apisupport/project/NbModuleProject.java +++ a/apisupport.ant/src/org/netbeans/modules/apisupport/project/NbModuleProject.java @@ -934,6 +934,7 @@ } private static final String[] RECOMMENDED_TYPES = { + UIUtil.TEMPLATE_CATEGORY, "java-classes", // NOI18N "java-main-class", // NOI18N "java-forms", // NOI18N @@ -945,7 +946,6 @@ "ant-task", // NOI18N "junit", // NOI18N "simple-files", // NOI18N - UIUtil.TEMPLATE_CATEGORY, }; @Override public String[] getPrivilegedTemplates() { --- a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEProject.java +++ a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEProject.java @@ -99,6 +99,8 @@ import org.netbeans.spi.java.project.support.LookupMergerSupport; import org.netbeans.spi.java.project.support.ui.BrokenReferencesSupport; import org.netbeans.spi.project.AuxiliaryConfiguration; +import org.netbeans.spi.project.LookupProvider.Registration.ProjectType; +import org.netbeans.spi.project.ProjectServiceProvider; import org.netbeans.spi.project.ant.AntArtifactProvider; import org.netbeans.spi.project.ant.AntBuildExtenderFactory; import org.netbeans.spi.project.support.LookupProviderSupport; @@ -374,7 +376,6 @@ QuerySupport.createSources(this, helper, evaluator(), getSourceRoots(), getTestSourceRoots(), Roots.nonSourceRoots(ProjectProperties.BUILD_DIR, J2SEProjectProperties.DIST_DIR)), QuerySupport.createSharabilityQuery2(helper, evaluator(), getSourceRoots(), getTestSourceRoots()), new CoSAwareFileBuiltQueryImpl(QuerySupport.createFileBuiltQuery(helper, evaluator(), getSourceRoots(), getTestSourceRoots()), this), - new RecommendedTemplatesImpl (this.updateHelper), ProjectClassPathModifier.extenderForModifier(cpMod), buildExtender, cpMod, @@ -734,13 +735,15 @@ } - private static final class RecommendedTemplatesImpl implements RecommendedTemplates, PrivilegedTemplates { - RecommendedTemplatesImpl (UpdateHelper helper) { - this.helper = helper; + @ProjectServiceProvider(service={RecommendedTemplates.class, PrivilegedTemplates.class}, projectTypes=@ProjectType(id="org-netbeans-modules-java-j2seproject", position=100)) + public static final class RecommendedTemplatesImpl implements RecommendedTemplates, PrivilegedTemplates { + + private final J2SEProject project; + + public RecommendedTemplatesImpl(Project project) { + this.project = (J2SEProject) project; } - private UpdateHelper helper; - // List of primarily supported templates private static final String[] APPLICATION_TYPES = new String[] { @@ -752,8 +755,6 @@ "persistence", // NOI18N "oasis-XML-catalogs", // NOI18N "XML", // NOI18N - "ant-script", // NOI18N - "ant-task", // NOI18N "web-service-clients", // NOI18N "REST-clients", // NOI18N "wsdl", // NOI18N @@ -761,6 +762,8 @@ // "web-types", // NOI18N "junit", // NOI18N // "MIDP", // NOI18N + "ant-script", // NOI18N + "ant-task", // NOI18N "simple-files" // NOI18N }; @@ -773,8 +776,6 @@ "persistence", // NOI18N "oasis-XML-catalogs", // NOI18N "XML", // NOI18N - "ant-script", // NOI18N - "ant-task", // NOI18N "servlet-types", // NOI18N "servlet-types-j2se-only",// NOI18N "web-service-clients", // NOI18N @@ -783,6 +784,8 @@ // "web-types", // NOI18N "junit", // NOI18N // "MIDP", // NOI18N + "ant-script", // NOI18N + "ant-task", // NOI18N "simple-files" // NOI18N }; @@ -800,7 +803,7 @@ @Override public String[] getRecommendedTypes() { - EditableProperties ep = helper.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH); + EditableProperties ep = project.getUpdateHelper().getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH); // if the project has no main class, it's not really an application boolean isLibrary = ep.getProperty (ProjectProperties.MAIN_CLASS) == null || "".equals (ep.getProperty (ProjectProperties.MAIN_CLASS)); // NOI18N return isLibrary ? LIBRARY_TYPES : APPLICATION_TYPES; --- a/javafx2.project/src/org/netbeans/modules/javafx2/project/JFXRecommendedTemplates.java +++ a/javafx2.project/src/org/netbeans/modules/javafx2/project/JFXRecommendedTemplates.java @@ -41,6 +41,7 @@ */ package org.netbeans.modules.javafx2.project; +import org.netbeans.spi.project.LookupProvider.Registration.ProjectType; import org.netbeans.spi.project.ProjectServiceProvider; import org.netbeans.spi.project.ui.RecommendedTemplates; @@ -48,9 +49,7 @@ * * @author Tomas Zezula */ -@ProjectServiceProvider( - service=RecommendedTemplates.class, - projectType={"org-netbeans-modules-java-j2seproject"}) +@ProjectServiceProvider(service=RecommendedTemplates.class, projectTypes=@ProjectType(id="org-netbeans-modules-java-j2seproject", position=200)) public class JFXRecommendedTemplates implements RecommendedTemplates { private static final String[] RECOMMENDED_TEMPLATES = { --- a/javawebstart/src/org/netbeans/modules/javawebstart/package-info.java +++ a/javawebstart/src/org/netbeans/modules/javawebstart/package-info.java @@ -36,7 +36,7 @@ * Portions Copyrighted 2011 Sun Microsystems, Inc. */ -@TemplateRegistration(folder="Other", content="JnlpTemplate.jnlp", category="java-classes", displayName="#JnlpTemplate", description="resources/JNLPTemplate.html", position=500) +@TemplateRegistration(folder="Classes", position=2000, content="JnlpTemplate.jnlp", category="java-classes", displayName="#JnlpTemplate", description="resources/JNLPTemplate.html") @Messages("JnlpTemplate=JNLP File") package org.netbeans.modules.javawebstart; --- a/maven.apisupport/src/org/netbeans/modules/maven/apisupport/ApisupportRecoPrivTemplates.java +++ a/maven.apisupport/src/org/netbeans/modules/maven/apisupport/ApisupportRecoPrivTemplates.java @@ -44,6 +44,7 @@ import org.netbeans.modules.apisupport.project.api.UIUtil; import org.netbeans.modules.maven.api.NbMavenProject; +import org.netbeans.spi.project.LookupProvider.Registration.ProjectType; import org.netbeans.spi.project.ProjectServiceProvider; import org.netbeans.spi.project.ui.PrivilegedTemplates; import org.netbeans.spi.project.ui.RecommendedTemplates; @@ -52,7 +53,7 @@ * apisupport specific part of RecommendedTemplates and PrivilegedTemplates, * @author Milos Kleint */ -@ProjectServiceProvider(service={RecommendedTemplates.class, PrivilegedTemplates.class}, projectType="org-netbeans-modules-maven/" + NbMavenProject.TYPE_NBM) +@ProjectServiceProvider(service={RecommendedTemplates.class, PrivilegedTemplates.class}, projectTypes=@ProjectType(id="org-netbeans-modules-maven/" + NbMavenProject.TYPE_NBM, position=100)) public class ApisupportRecoPrivTemplates implements RecommendedTemplates, PrivilegedTemplates { private static final String[] NBM_PRIVILEGED_NAMES = { @@ -66,16 +67,16 @@ //"Templates/Other/properties.properties", // NOI18N }; private static final String[] NBM_TYPES = { + UIUtil.TEMPLATE_CATEGORY, "java-classes", // NOI18N "java-main-class", // NOI18N "java-forms", // NOI18N "java-beans", // NOI18N "oasis-XML-catalogs", // NOI18N "XML", // NOI18N - "junit", // NOI18N + "junit", // NOI18N "simple-files", // NOI18N "REST-clients", // NOI18N - UIUtil.TEMPLATE_CATEGORY, }; --- a/maven.persistence/src/org/netbeans/modules/maven/persistence/RecommendedTemplatesImpl.java +++ a/maven.persistence/src/org/netbeans/modules/maven/persistence/RecommendedTemplatesImpl.java @@ -42,10 +42,11 @@ package org.netbeans.modules.maven.persistence; +import org.netbeans.spi.project.LookupProvider.Registration.ProjectType; import org.netbeans.spi.project.ProjectServiceProvider; import org.netbeans.spi.project.ui.RecommendedTemplates; -@ProjectServiceProvider(service=RecommendedTemplates.class, projectType="org-netbeans-modules-maven") +@ProjectServiceProvider(service=RecommendedTemplates.class, projectTypes=@ProjectType(id="org-netbeans-modules-maven", position=700)) public class RecommendedTemplatesImpl implements RecommendedTemplates { public String[] getRecommendedTypes() { return new String[] {"persistence"}; --- a/maven/src/org/netbeans/modules/maven/queries/RecommendedTemplatesImpl.java +++ a/maven/src/org/netbeans/modules/maven/queries/RecommendedTemplatesImpl.java @@ -48,11 +48,12 @@ import org.netbeans.api.project.Project; import org.netbeans.api.project.ProjectUtils; import org.netbeans.modules.maven.api.NbMavenProject; +import org.netbeans.spi.project.LookupProvider.Registration.ProjectType; import org.netbeans.spi.project.ProjectServiceProvider; import org.netbeans.spi.project.ui.PrivilegedTemplates; import org.netbeans.spi.project.ui.RecommendedTemplates; -@ProjectServiceProvider(service={RecommendedTemplates.class, PrivilegedTemplates.class}, projectType="org-netbeans-modules-maven") +@ProjectServiceProvider(service={RecommendedTemplates.class, PrivilegedTemplates.class}, projectTypes=@ProjectType(id="org-netbeans-modules-maven", position=100)) public final class RecommendedTemplatesImpl implements RecommendedTemplates, PrivilegedTemplates { private static final String[] JAR_APPLICATION_TYPES = { --- a/nbbuild/antsrc/org/netbeans/nbbuild/LayerIndex.java +++ a/nbbuild/antsrc/org/netbeans/nbbuild/LayerIndex.java @@ -51,12 +51,15 @@ import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.StringReader; +import java.text.Collator; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.Set; @@ -110,6 +113,11 @@ serviceOutput = f; } + private File templatesOutput; + public void setTemplatesOutput(File f) { + templatesOutput = f; + } + private String resourceId; private List resources; /** If this parameter is provided, then this tasks creates a resource @@ -129,6 +137,7 @@ SortedMap files = new TreeMap(); // layer path -> cnb SortedMap> labels = new TreeMap>(); // layer path -> cnb -> label final Map positions = new TreeMap(); // layer path -> position + SortedMap> templateCategories = new TreeMap>(Collator.getInstance(Locale.ENGLISH)); // templateCategory item (or "") -> layer paths SortedMap>> serviceImpls = new TreeMap>>(); // path -> interface -> [impl] Map servicePositions = new HashMap(); // impl -> position for (FileSet fs : filesets) { @@ -154,7 +163,7 @@ ZipResource res = new LayerResource(jar, layer, layer.replaceFirst("/[^/]+$", "").replace('/', '.') + ".xml"); resources.add(res); } else { - parse(jf.getInputStream(jf.getEntry(layer)), files, labels, positions, cnb, jf); + parse(jf.getInputStream(jf.getEntry(layer)), files, labels, positions, templateCategories, cnb, jf); } } ZipEntry generatedLayer = jf.getEntry("META-INF/generated-layer.xml"); @@ -163,7 +172,7 @@ ZipResource res = new LayerResource(jar, generatedLayer.getName(), cnb + "-generated.xml"); resources.add(res); } else { - parse(jf.getInputStream(generatedLayer), files, labels, positions, cnb + "@", jf); + parse(jf.getInputStream(generatedLayer), files, labels, positions, templateCategories, cnb + "@", jf); } } if (serviceOutput != null) { @@ -189,6 +198,9 @@ if (serviceOutput != null) { writeServiceIndex(serviceImpls, servicePositions); } + if (templatesOutput != null) { + writeTemplateIndex(templateCategories, labels, positions); + } } catch (IOException x) { throw new BuildException(x, getLocation()); } @@ -213,12 +225,14 @@ } private void parse(InputStream is, final Map files, final SortedMap> labels, - final Map positions, final String cnb, final JarFile jf) throws Exception { + final Map positions, final Map> templateCategories, final String cnb, final JarFile jf) throws Exception { SAXParserFactory f = SAXParserFactory.newInstance(); f.setValidating(false); f.setNamespaceAware(false); f.newSAXParser().parse(is, new DefaultHandler() { String prefix = ""; + List templateCategory; + boolean template; void register(String path) { if (!files.containsKey(path)) { files.put(path, cnb); @@ -270,6 +284,10 @@ throw new SAXException(x); } } + } else if (qName.equals("attr") && attributes.getValue("name").equals("template")) { + template = Boolean.parseBoolean(attributes.getValue("boolvalue")); + } else if (qName.equals("attr") && attributes.getValue("name").equals("templateCategory")) { + templateCategory = Arrays.asList(attributes.getValue("stringvalue").split(" *, *")); } } private void loadDisplayName(String bundle, String key) throws SAXException { @@ -308,9 +326,32 @@ if (qName.equals("folder")) { prefix = prefix.replaceFirst("[^/]+/$", ""); } else if (qName.equals("file")) { + // XXX really want to track simple=false, and templateCategory on folders + if (template && prefix.matches("Templates/(?!Project/|Licenses/|Services/|JSF/JSF_From_Entity_Wizard/|JSF/JSF_From_Entity_Snippets/).+")) { + if (templateCategory != null) { + for (String category : templateCategory) { + if (category.equals("invisible")) { + continue; + } + registerTemplateCategory(category); + } + } else { + registerTemplateCategory(""); + } + template = false; + } + templateCategory = null; prefix = prefix.replaceFirst("[^/]+$", ""); } } + void registerTemplateCategory(String category) { + Set paths = templateCategories.get(category); + if (paths == null) { + paths = new TreeSet(); + templateCategories.put(category, paths); + } + paths.add(prefix); + } @Override public InputSource resolveEntity(String pub, String sys) throws IOException, SAXException { return new InputSource(new StringReader("")); @@ -615,4 +656,43 @@ } } + private void writeTemplateIndex(Map> templateCategories, SortedMap> labels, Map positions) throws IOException { + PrintWriter pw = new PrintWriter(templatesOutput, "UTF-8"); + Comparator order = new LayerPathComparator(positions); + for (Map.Entry> categoryEntry : templateCategories.entrySet()) { + pw.print("CATEGORY "); + String category = categoryEntry.getKey(); + pw.println(category.isEmpty() ? "" : "'" + category + "'"); + SortedMap> templatesByFolder = new TreeMap>(order); + for (String path : categoryEntry.getValue()) { + int slash = path.lastIndexOf('/'); + String folder = path.substring(0, slash + 1); + SortedSet templates = templatesByFolder.get(folder); + if (templates == null) { + templates = new TreeSet(order); + templatesByFolder.put(folder, templates); + } + templates.add(path); + } + for (Map.Entry> folderEntry : templatesByFolder.entrySet()) { + String folder = folderEntry.getKey(); + pw.print(" FOLDER " + folder); + printlnLabel(pw, folder, labels); + for (String template : folderEntry.getValue()) { + pw.print(" " + template.substring(folder.length())); + printlnLabel(pw, template, labels); + } + } + } + pw.close(); + log(templatesOutput + ": template index written"); + } + private static void printlnLabel(PrintWriter pw, String path, SortedMap> labels) { + SortedMap possibilities = labels.get(path); + if (possibilities != null && possibilities.size() == 1) { + pw.print(" (\"" + possibilities.values().iterator().next() + "\")"); + } + pw.println(); + } + } --- a/nbbuild/build.xml +++ a/nbbuild/build.xml @@ -1515,7 +1515,7 @@ - + --- a/nbbuild/javadoctools/disallowed-links.xml +++ a/nbbuild/javadoctools/disallowed-links.xml @@ -91,6 +91,6 @@ - + --- a/projectui/src/org/netbeans/modules/project/ui/OpenProjectList.java +++ a/projectui/src/org/netbeans/modules/project/ui/OpenProjectList.java @@ -1292,7 +1292,7 @@ if ( fo == null ) { it.remove(); // Does not exists remove } - else if ( isRecommended( rtNames, fo ) ) { + else if (isRecommended(rtNames, fo, true)) { result.add( fo ); privilegedTemplates.remove( templateName ); // Not to have it twice } @@ -1316,7 +1316,7 @@ } - static boolean isRecommended(@NonNull String[] recommendedTypes, @NonNull FileObject primaryFile) { + static boolean isRecommended(@NonNull String[] recommendedTypes, @NonNull FileObject primaryFile, boolean lax) { if (recommendedTypes.length == 0) { // if no recommendedTypes are supported (i.e. freeform) -> disaply all templates return true; @@ -1335,7 +1335,7 @@ } else { // issue 44871, if attr 'templateCategorized' is not set => all is ok // no category set, ok display it - return true; + return lax; } } --- a/projectui/src/org/netbeans/modules/project/ui/TemplateChooserPanelGUI.form +++ a/projectui/src/org/netbeans/modules/project/ui/TemplateChooserPanelGUI.form @@ -1,4 +1,4 @@ - +
--- a/projectui/src/org/netbeans/modules/project/ui/TemplateChooserPanelGUI.java +++ a/projectui/src/org/netbeans/modules/project/ui/TemplateChooserPanelGUI.java @@ -50,11 +50,15 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.swing.DefaultComboBoxModel; import javax.swing.ListCellRenderer; import javax.swing.event.ChangeListener; -import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.CheckForNull; import org.netbeans.api.project.Project; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; @@ -70,17 +74,13 @@ import org.openide.util.NbBundle; import org.openide.util.Utilities; -/** If you are looking for the non-GUI part of the panel please look - * into new file wizard - */ - - - // #89393: GTK needs cell renderer to implement UIResource to look "natively" /** * Provides the GUI for the template chooser panel. * @author Jesse Glick */ final class TemplateChooserPanelGUI extends javax.swing.JPanel implements PropertyChangeListener, AsyncGUIJob { + + private static final Logger LOG = Logger.getLogger(TemplateChooserPanelGUI.class.getName()); /** prefered dimmension of the panels */ private static final java.awt.Dimension PREF_DIM = new java.awt.Dimension (500, 340); @@ -94,7 +94,7 @@ //GUI Builder private TemplatesPanelGUI.Builder builder; private Project project; - private @NonNull String[] projectRecommendedTypes; + private String[] projectRecommendedTypes; private String category; private String template; private boolean isWarmUp = true; @@ -299,19 +299,35 @@ } @Override protected boolean createKeys(List keys) { - for (DataObject d : folder.getChildren()) { - if (isFolderOfTemplates(d)) { - boolean leaf = true; - for (DataObject child : ((DataFolder) d).getChildren()) { - if (isFolderOfTemplates(child)) { - leaf = false; - break; + Set added = new HashSet(); + DataObject[] potentialCategories = folder.getChildren(); + if (projectRecommendedTypes == null) { + return true; // in case readValues has not been called + } + for (String type : projectRecommendedTypes) { + addMatches(potentialCategories, added, new String[] {type}, false, type, keys); + } + addMatches(potentialCategories, added, projectRecommendedTypes, true, "", keys); + return true; + } + private void addMatches(DataObject[] potentialCategories, Set added, String[] types, boolean lax, String typeDiagnostic, List keys) { + for (DataObject d : potentialCategories) { + if (!added.contains(d)) { + DataObject trigger = isFolderOfTemplates(d, types, lax); + if (trigger != null) { + added.add(d); + LOG.log(Level.FINE, "adding {0} because of {1} matched by {2}", new Object[] {d.getPrimaryFile().getPath(), typeDiagnostic, trigger.getPrimaryFile().getPath()}); + boolean leaf = true; + for (DataObject child : ((DataFolder) d).getChildren()) { + if (isFolderOfTemplates(child, projectRecommendedTypes, true) != null) { + leaf = false; + break; + } } + keys.add(new TemplateKey(d, leaf)); } - keys.add(new TemplateKey(d, leaf)); } } - return true; } @Override protected Node createNodeForKey(TemplateKey k) { @@ -330,14 +346,14 @@ // Private methods ----------------------------------------------------- - private boolean isFolderOfTemplates(DataObject d) { + private @CheckForNull DataObject isFolderOfTemplates(DataObject d, String[] projectRecommendedTypes, boolean lax) { if (d instanceof DataFolder && !isTemplate((DataFolder)d)) { Object o = d.getPrimaryFile().getAttribute("simple"); // NOI18N if (o == null || Boolean.TRUE.equals(o)) { - return hasChildren((Project) projectsComboBox.getSelectedItem(), d); + return hasChildren(getProject(), d, projectRecommendedTypes, lax); } } - return false; + return null; } } @@ -353,7 +369,7 @@ @Override protected boolean createKeys(List keys) { for (DataObject dobj : root.getChildren()) { - if (isTemplate(dobj) && OpenProjectList.isRecommended(projectRecommendedTypes, dobj.getPrimaryFile())) { + if (isTemplate(dobj) && OpenProjectList.isRecommended(projectRecommendedTypes, dobj.getPrimaryFile(), true)) { if (dobj instanceof DataShadow) { dobj = ((DataShadow) dobj).getOriginal(); } @@ -410,33 +426,32 @@ return false; } - private boolean hasChildren (Project p, DataObject folder) { + private @CheckForNull DataObject hasChildren(Project p, DataObject folder, String[] projectRecommendedTypes, boolean lax) { if (!(folder instanceof DataFolder)) { - return false; + return null; } DataFolder f = (DataFolder) folder; - if (!OpenProjectList.isRecommended(projectRecommendedTypes, f.getPrimaryFile())) { + if (!OpenProjectList.isRecommended(projectRecommendedTypes, f.getPrimaryFile(), true)) { // Eg. Licenses folder. //see #102508 - return false; + return null; } DataObject[] ch = f.getChildren (); for (int i = 0; i < ch.length; i++) { - if (isTemplate (ch[i]) && OpenProjectList.isRecommended(projectRecommendedTypes, ch[i].getPrimaryFile ())) { + if (isTemplate(ch[i]) && OpenProjectList.isRecommended(projectRecommendedTypes, ch[i].getPrimaryFile(), lax)) { // XXX: how to filter link to Package template in each java types folder? if (!(ch[i] instanceof DataShadow)) { - return true; + return ch[i]; } - } else if (ch[i] instanceof DataFolder && hasChildren (p, ch[i])) { - return true; + } else if (ch[i] instanceof DataFolder) { + DataObject t = hasChildren(p, ch[i], projectRecommendedTypes, lax); + if (t != null) { + return t; + } } } - return false; - - // simplied but more counts - //return new FileChildren (p, (DataFolder) folder).getNodesCount () > 0; - + return null; } public void construct () { --- a/projectui/test/unit/src/org/netbeans/modules/project/ui/TemplateChooserPanelGUITest.java +++ a/projectui/test/unit/src/org/netbeans/modules/project/ui/TemplateChooserPanelGUITest.java @@ -43,6 +43,7 @@ package org.netbeans.modules.project.ui; import java.lang.ref.WeakReference; +import java.util.logging.Level; import org.netbeans.api.project.Project; import org.netbeans.junit.NbTestCase; import org.netbeans.modules.project.ui.TemplateChooserPanelGUI.FileChooserBuilder; @@ -71,6 +72,14 @@ return 300000; } + @Override protected Level logLevel() { + return Level.FINE; + } + + @Override protected String logRoot() { + return "org.netbeans.modules.project.ui"; + } + public void testReadValues() { Project p = new P(); String category = ""; @@ -123,8 +132,6 @@ t.setAttribute("template", true); t.setAttribute("templateCategory", "main"); TemplateChooserPanelGUI gui = new TemplateChooserPanelGUI(); - gui.construct(); - gui.finished(); gui.readValues(new P(), null, null); FileChooserBuilder builder = gui.new FileChooserBuilder(); assertChildren("Main, Samples[Main]", builder.createCategoriesChildren(DataFolder.findFolder(r))); @@ -132,6 +139,34 @@ assertChildren("t5", builder.createTemplatesChildren(DataFolder.findFolder(r.getFileObject("Samples/Main")))); } + public void testTemplateCategoryOrder() throws Exception { // #206126 + FileObject r = FileUtil.createMemoryFileSystem().getRoot(); + FileObject f = r.createFolder("Specialized"); + f.setAttribute("position", 1); + FileObject t = f.createData("t1"); + t.setAttribute("template", true); + t.setAttribute("templateCategory", "inappropriate"); + t = f.createData("t2"); + t.setAttribute("template", true); + t.setAttribute("templateCategory", "misc"); + f = r.createFolder("Common"); + f.setAttribute("position", 2); + t = f.createData("t3"); + t.setAttribute("template", true); + t.setAttribute("templateCategory", "main"); + t = f.createData("t4"); + t.setAttribute("template", true); + t.setAttribute("templateCategory", "misc"); + f = r.createFolder("Plain"); + f.setAttribute("position", 3); + t = f.createData("t5"); + t.setAttribute("template", true); + TemplateChooserPanelGUI gui = new TemplateChooserPanelGUI(); + gui.readValues(new P(), null, null); + FileChooserBuilder builder = gui.new FileChooserBuilder(); + assertChildren("Common, Specialized, Plain", builder.createCategoriesChildren(DataFolder.findFolder(r))); + } + private static void assertChildren(String repn, Children c) { StringBuilder b = new StringBuilder(); representationOf(c, b); --- a/projectuiapi/apichanges.xml +++ a/projectuiapi/apichanges.xml @@ -107,6 +107,34 @@ + + + Order of RecommendedTemplates significant + + + + +

+ Existing implementations should be reviewed and the list of categories possibly reordered. + (In cases where UILookupMergerSupport.createRecommendedTemplatesMerger was in use, + it will generally be necessary to use ProjectServiceProvider for all implementations, + rather than supplying a basic implementation in the project's base lookup.) +

+

+ Positions of template folders may also need to be adjusted. +

+
+ +

+ UI infrastructure will now pay attention to the list of categories + in RecommendedTemplates, preferring to show template + folders first which contain templates marked with categories earlier + in the list. +

+
+ + +
Adding ability to create a file sensitive action with custom performer --- a/projectuiapi/nbproject/project.properties +++ a/projectuiapi/nbproject/project.properties @@ -42,7 +42,7 @@ javac.compilerargs=-Xlint -Xlint:-serial javac.source=1.6 -spec.version.base=1.56.0 +spec.version.base=1.57.0 is.autoload=true javadoc.arch=${basedir}/arch.xml javadoc.apichanges=${basedir}/apichanges.xml --- a/projectuiapi/src/org/netbeans/spi/project/ui/RecommendedTemplates.java +++ a/projectuiapi/src/org/netbeans/spi/project/ui/RecommendedTemplates.java @@ -59,6 +59,7 @@ /** * Lists supported template types. + *

Since 1.57 the order is significant and may be considered by UI infrastructure. * @return types of supported templates (should match template file attribute names) * @see TemplateRegistration#category */ --- a/projectuiapi/src/org/netbeans/spi/project/ui/templates/support/package.html +++ a/projectuiapi/src/org/netbeans/spi/project/ui/templates/support/package.html @@ -100,11 +100,21 @@ There is currently no annotation to define template folders; they should be defined in the XML layer as subfolders of Templates with displayName and position attributes. +

+The position of template folders is considered in the UI as a secondary sort key; +the order of categories specified in RecommendedTemplates is primary. +Use -J-Dorg.netbeans.modules.project.ui.TemplateChooserPanelGUI.level=FINE +to see why particular folders are chosen in a particular order. +

Note that it is possible to set the attribute simple to (boolean) false in order to hide templates from the list shown in the standard wizards, while still making it possible to use TemplateRegistration and apply templates programmatically. To hide templates from the "new" wizards but show them in Template Manager, just set a category which is never used (such as invisible). +

+A summary of templates and categories in the NetBeans IDE can be found online: +templates.txt +

There are two important interfaces affecting the behavior of the templates. Implementation of these interfaces should reside in the project's lookup. --- a/spring.beans/src/org/netbeans/modules/spring/beans/RecommendedTemplatesImpl.java +++ a/spring.beans/src/org/netbeans/modules/spring/beans/RecommendedTemplatesImpl.java @@ -42,6 +42,7 @@ package org.netbeans.modules.spring.beans; +import org.netbeans.spi.project.LookupProvider.Registration.ProjectType; import org.netbeans.spi.project.ProjectServiceProvider; import org.netbeans.spi.project.ui.RecommendedTemplates; @@ -65,14 +66,14 @@ this.web = web; } - @ProjectServiceProvider(service=RecommendedTemplates.class, projectType="org-netbeans-modules-web-project") + @ProjectServiceProvider(service=RecommendedTemplates.class, projectTypes=@ProjectType(id="org-netbeans-modules-web-project", position=1000)) public static RecommendedTemplates forWeb() { return new RecommendedTemplatesImpl(true); } - @ProjectServiceProvider(service=RecommendedTemplates.class, projectType={ - "org-netbeans-modules-java-j2seproject", - "org-netbeans-modules-j2ee-ejbjarproject" + @ProjectServiceProvider(service=RecommendedTemplates.class, projectTypes={ + @ProjectType(id="org-netbeans-modules-java-j2seproject", position=1000), + @ProjectType(id="org-netbeans-modules-j2ee-ejbjarproject", position=1000) }) public static RecommendedTemplates forNonWeb() { return new RecommendedTemplatesImpl(false); --- a/web.project/src/org/netbeans/modules/web/project/WebProject.java +++ a/web.project/src/org/netbeans/modules/web/project/WebProject.java @@ -172,6 +172,8 @@ import org.netbeans.spi.java.project.support.ExtraSourceJavadocSupport; import org.netbeans.spi.java.project.support.LookupMergerSupport; import org.netbeans.spi.java.project.support.ui.BrokenReferencesSupport; +import org.netbeans.spi.project.LookupProvider.Registration.ProjectType; +import org.netbeans.spi.project.ProjectServiceProvider; import org.netbeans.spi.whitelist.support.WhiteListQueryMergerSupport; import org.netbeans.spi.project.support.ant.PropertyProvider; import org.netbeans.spi.project.support.ant.PropertyUtils; @@ -571,7 +573,6 @@ Roots.nonSourceRoots(ProjectProperties.BUILD_DIR, WebProjectProperties.DIST_DIR)), QuerySupport.createSharabilityQuery(helper, evaluator(), getSourceRoots(), getTestSourceRoots(), WebProjectProperties.WEB_DOCBASE_DIR), - new RecommendedTemplatesImpl(this), new CoSAwareFileBuiltQueryImpl(QuerySupport.createFileBuiltQuery(helper, evaluator(), getSourceRoots(), getTestSourceRoots()), this), ProjectClassPathModifier.extenderForModifier(cpMod), buildExtender, @@ -1276,6 +1277,8 @@ // List of primarily supported templates private static final String[] TYPES = new String[] { + "servlet-types", // NOI18N + // XXX consider splitting RecommendedTemplatesImpl into two, one returning high-priority categories plus TYPES_EJB, the other returning low-priority categories "java-classes", // NOI18N "java-main-class", // NOI18N "java-forms", // NOI18N @@ -1286,7 +1289,6 @@ "ant-script", // NOI18N "ant-task", // NOI18N "REST-clients", // NOI18N - "servlet-types", // NOI18N "web-types", // NOI18N "web-types-server", // NOI18N "web-services", // NOI18N @@ -1403,12 +1405,13 @@ privilegedTemplates.addAll(Arrays.asList(PRIVILEGED_NAMES)); } - private final class RecommendedTemplatesImpl implements RecommendedTemplates, PrivilegedTemplates { + @ProjectServiceProvider(service={RecommendedTemplates.class, PrivilegedTemplates.class}, projectTypes=@ProjectType(id="org-netbeans-modules-web-project", position=100)) + public static final class RecommendedTemplatesImpl implements RecommendedTemplates, PrivilegedTemplates { private WebProject project; private J2eeProjectCapabilities projectCap; - RecommendedTemplatesImpl (WebProject project) { - this.project = project; + public RecommendedTemplatesImpl(Project project) { + this.project = (WebProject) project; } private boolean checked = false; @@ -1421,7 +1424,7 @@ if (isArchive) { return TYPES_ARCHIVE; } else if (projectCap.isEjb31LiteSupported()){ - List list = new ArrayList(Arrays.asList(TYPES)); + List list = new ArrayList(Arrays.asList(TYPES)); if (projectCap.isEjb31Supported() || serverSupportsEJB31){ list.addAll(Arrays.asList(TYPES_EJB)); } else { @@ -1440,16 +1443,16 @@ } else { List list; if (projectCap.isEjb31LiteSupported()) { - list = getPrivilegedTemplatesEE5(); + list = project.getPrivilegedTemplatesEE5(); if (projectCap.isEjb31Supported() || serverSupportsEJB31){ list.addAll(13, Arrays.asList(PRIVILEGED_NAMES_EE6_FULL)); } else { list.addAll(13, Arrays.asList(PRIVILEGED_NAMES_EE6_WEB)); } } else if (isEE5){ - list = getPrivilegedTemplatesEE5(); + list = project.getPrivilegedTemplatesEE5(); } else { - list = WebProject.this.getPrivilegedTemplates(); + list = project.getPrivilegedTemplates(); } return list.toArray(new String[list.size()]); } @@ -1457,13 +1460,13 @@ private void checkEnvironment() { if (!checked) { - final Object srcType = helper.getStandardPropertyEvaluator(). + final Object srcType = project.helper.getStandardPropertyEvaluator(). getProperty(WebProjectProperties.JAVA_SOURCE_BASED); if ("false".equals(srcType)) { isArchive = true; } projectCap = J2eeProjectCapabilities.forProject(project); - Profile profile = Profile.fromPropertiesString(eval.getProperty(WebProjectProperties.J2EE_PLATFORM)); + Profile profile = Profile.fromPropertiesString(project.eval.getProperty(WebProjectProperties.J2EE_PLATFORM)); isEE5 = profile == Profile.JAVA_EE_5; serverSupportsEJB31 = Util.getSupportedProfiles(project).contains(Profile.JAVA_EE_6_FULL); checked = true;