diff -r 0f6ce06434f3 openide.awt/src/org/netbeans/modules/openide/awt/ActionProcessor.java --- a/openide.awt/src/org/netbeans/modules/openide/awt/ActionProcessor.java Mon Nov 22 10:31:09 2010 +0100 +++ b/openide.awt/src/org/netbeans/modules/openide/awt/ActionProcessor.java Mon Nov 22 23:51:26 2010 +0100 @@ -277,7 +277,13 @@ if (e.getAnnotation(ActionRegistration.class) != null) { continue; } - throw new LayerGenerationException("Don't use @ActionReference without @ActionRegistration", e); + ActionID id = e.getAnnotation(ActionID.class); + if (id != null) { + ActionReference ref = e.getAnnotation(ActionReference.class); + processReferences(e, ref, id); + continue; + } + throw new LayerGenerationException("Don't use @ActionReference without @ActionID", e); } for (Element e : roundEnv.getElementsAnnotatedWith(ActionReferences.class)) { if (e.getAnnotation(ActionRegistration.class) != null) { diff -r 0f6ce06434f3 openide.windows/nbproject/project.xml --- a/openide.windows/nbproject/project.xml Mon Nov 22 10:31:09 2010 +0100 +++ b/openide.windows/nbproject/project.xml Mon Nov 22 23:51:26 2010 +0100 @@ -58,6 +58,11 @@ + org.openide.filesystems + + + + org.openide.nodes diff -r 0f6ce06434f3 openide.windows/src/org/netbeans/modules/openide/windows/TopComponentProcessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openide.windows/src/org/netbeans/modules/openide/windows/TopComponentProcessor.java Mon Nov 22 23:51:26 2010 +0100 @@ -0,0 +1,165 @@ +/* + * 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.openide.windows; + +import java.util.HashSet; +import java.util.Set; +import javax.annotation.processing.Processor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.ExecutableType; +import org.openide.awt.ActionID; +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; +import org.openide.windows.TopComponent; +import org.openide.windows.TopComponent.Description; + +@SupportedSourceVersion(SourceVersion.RELEASE_6) +@ServiceProvider(service=Processor.class) +public final class TopComponentProcessor extends LayerGeneratingProcessor { + public TopComponentProcessor() { + } + + @Override + public Set getSupportedAnnotationTypes() { + Set hash = new HashSet(); + hash.add(TopComponent.Registration.class.getCanonicalName()); + hash.add(TopComponent.OpenActionRegistration.class.getCanonicalName()); + hash.add(TopComponent.Description.class.getCanonicalName()); + return hash; + } + + @Override + protected boolean handleProcess(Set annotations, RoundEnvironment roundEnv) throws LayerGenerationException { + for (Element e : roundEnv.getElementsAnnotatedWith(TopComponent.Registration.class)) { + TopComponent.Registration reg = e.getAnnotation(TopComponent.Registration.class); + assert reg != null; + + Description info = findInfo(e); + String id = info.preferredID().replace('.', '-'); + + File settingsFile = layer(e). + file("Windows2/Components/" + id + ".settings"). + contents(settingsFile(e)); + settingsFile.write(); + + File modeFile = layer(e). + file("Windows2/Modes/" + reg.mode() + "/" + id + ".wstcref"). + contents(modeFile(info.preferredID(), reg.openAtStartup())); + modeFile.write(); + } + + for (Element e : roundEnv.getElementsAnnotatedWith(TopComponent.OpenActionRegistration.class)) { + TopComponent.OpenActionRegistration reg = e.getAnnotation(TopComponent.OpenActionRegistration.class); + assert reg != null; + Description info = findInfo(e); + + ActionID aid = e.getAnnotation(ActionID.class); + if (aid != null) { + File actionFile = layer(e). + file("Actions/" + aid.category() + "/" + aid.id().replace('.', '-') + ".instance"). + methodvalue("instanceCreate", "org.openide.windows.TopComponent", "openAction"); + actionFile.instanceAttribute("component", TopComponent.class); + if (reg.preferredID().length() > 0) { + actionFile.stringvalue("preferredID", reg.preferredID()); + } + actionFile.bundlevalue("displayName", reg.displayName()); + if (info != null && info.iconBase().length() > 0) { + actionFile.stringvalue("iconBase", info.iconBase()); + } + actionFile.write(); + } + } + return true; + } + + private Description findInfo(Element e) throws LayerGenerationException { + Element type; + switch (e.asType().getKind()) { + case DECLARED: type = e; break; + case EXECUTABLE: type = ((DeclaredType)((ExecutableType)e.asType()).getReturnType()).asElement(); break; + default: throw new LayerGenerationException("" + e.asType().getKind(), e); + } + TopComponent.Description info = type.getAnnotation(TopComponent.Description.class); + return info; + } + + private static String settingsFile(Element e) throws LayerGenerationException { + String clazz, method; + switch (e.getKind()) { + case CLASS: clazz = e.toString(); method = null; break; + case METHOD: clazz = e.getEnclosingElement().toString(); method = e.getSimpleName().toString(); break; + default: + throw new LayerGenerationException("Cannot work on given element", e); + } + StringBuilder sb = new StringBuilder(); + sb.append("\n"); + sb.append("\n"); + sb.append("\n"); + sb.append(" \n"); + sb.append("\n"); + return sb.toString(); + } + + private static String modeFile(String id, boolean openAtStart) + throws LayerGenerationException { + StringBuilder sb = new StringBuilder(); + sb.append("\n"); + sb.append("\n"); + sb.append("\n"); + sb.append(" \n"); + sb.append(" \n"); + sb.append("\n"); + return sb.toString(); + } +} diff -r 0f6ce06434f3 openide.windows/src/org/openide/windows/OpenComponentAction.java --- a/openide.windows/src/org/openide/windows/OpenComponentAction.java Mon Nov 22 10:31:09 2010 +0100 +++ b/openide.windows/src/org/openide/windows/OpenComponentAction.java Mon Nov 22 23:51:26 2010 +0100 @@ -47,6 +47,8 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Map; +import org.openide.util.Exceptions; +import org.openide.util.Lookup; /** Opens a top component. * @@ -71,9 +73,18 @@ if (component != null) { return component; } - component = (TopComponent)map.get("component"); // NOI18N - assert component != null : "Component cannot be created for " + map; - return component; + TopComponent c = null; + Object id = map.get("preferredID"); // NOI18N + if (id instanceof String) { + c = WindowManager.getDefault().findTopComponent((String)id); + } + if (c == null) { + c = (TopComponent)map.get("component"); + } + if (id != null) { + component = c; + } + return c; } public void actionPerformed(ActionEvent e) { diff -r 0f6ce06434f3 openide.windows/src/org/openide/windows/TopComponent.java --- a/openide.windows/src/org/openide/windows/TopComponent.java Mon Nov 22 10:31:09 2010 +0100 +++ b/openide.windows/src/org/openide/windows/TopComponent.java Mon Nov 22 23:51:26 2010 +0100 @@ -60,6 +60,10 @@ import java.io.ObjectOutputStream; import java.io.ObjectStreamException; import java.io.Serializable; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.lang.reflect.InvocationTargetException; @@ -85,6 +89,7 @@ import javax.swing.Timer; import javax.swing.plaf.basic.BasicHTML; import javax.swing.text.Keymap; +import org.openide.awt.ActionID; import org.openide.awt.Actions; import org.openide.awt.UndoRedo; import org.openide.nodes.Node; @@ -92,6 +97,7 @@ import org.openide.nodes.NodeListener; import org.openide.util.ContextAwareAction; import org.openide.util.HelpCtx; +import org.openide.util.ImageUtilities; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.NbPreferences; @@ -429,6 +435,11 @@ * @since 4.20 */ public int getPersistenceType() { + Description info = getClass().getAnnotation(Description.class); + if (info != null) { + return info.persistenceType(); + } + //First check for 'PersistenceType' client property for compatibility. if (warnedClasses.add(getClass()) && !TopComponent.class.equals(getClass())) { Logger.getAnonymousLogger().warning( @@ -704,6 +715,7 @@ } /** + * Rather than overriding this method, consider using {@link Description}. * Subclasses are encouraged to override this method to provide preferred value * for unique TopComponent ID returned by {@link org.openide.windows.WindowManager#findTopComponentID}. * @@ -716,7 +728,11 @@ * @since 4.13 */ protected String preferredID() { - Class clazz = getClass(); + Class clazz = getClass(); + Description id = clazz.getAnnotation(Description.class); + if (id != null) { + return id.preferredID(); + } if (getPersistenceType() != PERSISTENCE_NEVER && warnedTCPIClasses.add(clazz)) { Logger.getAnonymousLogger().warning( @@ -1029,6 +1045,10 @@ /** @return The icon of the top component */ public Image getIcon() { + Description id; + if (icon == null && (id = getClass().getAnnotation(Description.class)) != null) { + icon = ImageUtilities.loadImage(id.iconBase(), true); + } return icon; } @@ -1357,6 +1377,62 @@ */ public TopComponent cloneComponent(); } + + /** Provides basic information about the persistence of a {@link TopComponent}. + * Using this annotation is preferred to overriding {@link #preferredID()} + * or calling {@link #setIcon(java.awt.Image)}. + * @since 6.36 + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public static @interface Description { + /** The default value for {@link TopComponent#preferredID()}. + */ + public String preferredID(); + /** The icon to load for {@link TopComponent#getIcon()}. + */ + public String iconBase() default ""; + /** Default value for {@link TopComponent#getPersistenceType()}. + * + * @return one of {@link TopComponent#PERSISTENCE_ALWAYS}, + * {@link TopComponent#PERSISTENCE_NEVER}, + * {@link TopComponent#PERSISTENCE_ONLY_OPENED} + * + */ + public int persistenceType() default PERSISTENCE_ALWAYS; + } + + /** Registers {@link TopComponent} into specified location. + * @since 6.36 + */ + @Retention(RetentionPolicy.SOURCE) + @Target({ ElementType.TYPE, ElementType.METHOD }) + public static @interface Registration { + /** Name of the mode the component shall be opened in */ + String mode(); + /** Shall the component be opened at start */ + boolean openAtStartup(); + } + + /** Creates an action that can open the component. + * The action is generated only + * if {@link ActionID} annotation is used on the same element, otherwise + * a compilation error is raised. + * @since 6.36 + */ + @Retention(RetentionPolicy.SOURCE) + @Target({ ElementType.TYPE, ElementType.METHOD }) + public static @interface OpenActionRegistration { + /** Display name of the action, usually can refer to value from a + * Bundle.properties using #KEY. + */ + String displayName(); + /** Shall the action first seek for a component with such ID + * before creating new instance of the component? + * @return the {@link TopComponent#preferredID()} to seek for + */ + String preferredID() default ""; + } /** Registry of all top components. * There is one instance that can be obtained via {@link TopComponent#getRegistry} diff -r 0f6ce06434f3 openide.windows/test/unit/src/org/netbeans/modules/openide/windows/Bundle.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openide.windows/test/unit/src/org/netbeans/modules/openide/windows/Bundle.properties Mon Nov 22 23:51:26 2010 +0100 @@ -0,0 +1,44 @@ +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +# +# Copyright 1997-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]" +# +# Contributor(s): +# +# The Original Software is NetBeans. The Initial Developer of the Original +# Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun +# Microsystems, Inc. All Rights Reserved. +# +# 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. + + +TEST_ACTION=Hello TC! diff -r 0f6ce06434f3 openide.windows/test/unit/src/org/netbeans/modules/openide/windows/TopComponentProcessorTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openide.windows/test/unit/src/org/netbeans/modules/openide/windows/TopComponentProcessorTest.java Mon Nov 22 23:51:26 2010 +0100 @@ -0,0 +1,155 @@ +/* + * 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.openide.windows; + +import java.awt.EventQueue; +import java.awt.event.ActionEvent; +import java.io.ByteArrayInputStream; +import java.util.Arrays; +import javax.swing.Action; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import org.netbeans.junit.NbTestCase; +import org.openide.awt.ActionID; +import org.openide.awt.ActionReference; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.windows.TopComponent; +import org.w3c.dom.Document; + +public class TopComponentProcessorTest extends NbTestCase { + + public TopComponentProcessorTest(String n) { + super(n); + } + + @Override + protected boolean runInEQ() { + return getName().contains("InEQ"); + } + + public void testTCRegisteredFine() throws Exception { + FileObject set = FileUtil.getConfigFile("Windows2/Components/my-tc.settings"); + assertNotNull("Settings file found", set); + assertValidate(set.asText()); + } + + public void testModeIsOK() throws Exception { + FileObject set = FileUtil.getConfigFile("Windows2/Modes/output/my-tc.wstcref"); + assertNotNull("Mode file found", set); + final String t = set.asText(); + assertValidate(t); + assertEquals("not opened, no true in there", -1, t.indexOf("true")); + } + + public void testFactoryRegisteredFine() throws Exception { + FileObject set = FileUtil.getConfigFile("Windows2/Components/factory-tc.settings"); + assertNotNull("Settings file found", set); + assertValidate(set.asText()); + } + + public void testFactoryModeIsOK() throws Exception { + FileObject set = FileUtil.getConfigFile("Windows2/Modes/explorer/factory-tc.wstcref"); + assertNotNull("Mode file found", set); + final String t = set.asText(); + assertValidate(t); + assertTrue("opened, no true in there", t.indexOf("true") > 0); + } + + public void testFactoryActionIsOKInEQ() throws Exception { + assertTrue("This one needs to run in EQT", EventQueue.isDispatchThread()); + FileObject fo = FileUtil.getConfigFile("Actions/Windows/open-factory-tc.instance"); + assertNotNull("Action generated", fo); + Action a = (Action)fo.getAttribute("instanceCreate"); + assertNotNull("Action found", a); + assertEquals("Not created yet", 0, TC.cnt); + a.actionPerformed(new ActionEvent(this, 0, "")); + assertEquals("Created", 1, TC.cnt); + assertEquals("Hello TC!", a.getValue(Action.NAME)); + assertEquals("any/iconbase.png", a.getValue("iconBase")); + assertEquals("preferredID found", "factory.tc", fo.getAttribute("preferredID")); + + FileObject dir = FileUtil.getConfigFile("Kuk/Huk"); + assertNotNull("Kuk/Huk found", dir); + FileObject ref = dir.getFileObject("open-factory-tc.shadow"); + assertNotNull("Reference found: " + Arrays.toString(dir.getChildren()), ref); + assertEquals(fo.getPath(), ref.getAttribute("originalFile")); + } + + private static void assertValidate(String xml) throws Exception { + DocumentBuilderFactory f = DocumentBuilderFactory.newInstance(); + f.setValidating(true); + DocumentBuilder b = f.newDocumentBuilder(); + Document res = b.parse(new ByteArrayInputStream(xml.getBytes("UTF-8"))); + assertNotNull("Parsed OK", res); + } + + @TopComponent.Registration( + mode="output", + openAtStartup=false + ) + @TopComponent.Description( + preferredID="my-tc", iconBase="org/openide/windows/Icon.png" + ) + public static class TC1 extends TopComponent { + } + + @TopComponent.Registration( + mode="explorer", + openAtStartup=true + ) + @TopComponent.Description( + preferredID="factory.tc", iconBase="any/iconbase.png" + ) + public static class TC extends TopComponent { + static int cnt; + + @ActionID(category="Windows", id="open.factory.tc") + @TopComponent.OpenActionRegistration(displayName="#TEST_ACTION") + @ActionReference(path="Kuk/Huk") + public static TC create() { + cnt++; + return new TC(); + } + } +} diff -r 0f6ce06434f3 openide.windows/test/unit/src/org/openide/windows/TopComponentTest.java --- a/openide.windows/test/unit/src/org/openide/windows/TopComponentTest.java Mon Nov 22 10:31:09 2010 +0100 +++ b/openide.windows/test/unit/src/org/openide/windows/TopComponentTest.java Mon Nov 22 23:51:26 2010 +0100 @@ -44,6 +44,7 @@ package org.openide.windows; +import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; @@ -66,6 +67,7 @@ import org.openide.nodes.Node; import org.openide.util.ContextAwareAction; import org.openide.util.HelpCtx; +import org.openide.util.ImageUtilities; import org.openide.util.Lookup; import org.openide.util.actions.CookieAction; import org.openide.util.io.NbMarshalledObject; @@ -83,6 +85,22 @@ public TopComponentTest(String testName) { super(testName); } + + public void testPreferredIDAndIconTakenFromAnnotation() { + @TopComponent.Description( + preferredID="my.id", iconBase="org/openide/windows/icon.png", + persistenceType=TopComponent.PERSISTENCE_ONLY_OPENED + ) + class MyTC extends TopComponent { + } + TopComponent tc = new MyTC(); + + assertEquals("ID is OK", "my.id", tc.preferredID()); + Image image = ImageUtilities.loadImage("org/openide/windows/icon.png"); + assertEquals("Image is OK", image, tc.getIcon()); + + assertEquals("Only opened", TopComponent.PERSISTENCE_ONLY_OPENED, tc.getPersistenceType()); + } public void testCanTCGarbageCollectWhenActionInMap() { TopComponent tc = new TopComponent();