diff --git a/apisupport.refactoring/src/org/netbeans/modules/apisupport/hints/DataObjectRegistrationHinter.java b/apisupport.refactoring/src/org/netbeans/modules/apisupport/hints/DataObjectRegistrationHinter.java new file mode 100644 --- /dev/null +++ b/apisupport.refactoring/src/org/netbeans/modules/apisupport/hints/DataObjectRegistrationHinter.java @@ -0,0 +1,142 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 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 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.apisupport.hints; + +import com.sun.source.tree.ModifiersTree; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Callable; +import javax.lang.model.element.Element; +import org.netbeans.api.java.source.GeneratorUtilities; +import org.netbeans.api.java.source.WorkingCopy; +import org.openide.DialogDisplayer; +import org.openide.NotifyDescriptor; +import org.openide.filesystems.FileObject; +import org.openide.loaders.DataObject; +import org.openide.util.NbBundle.Messages; +import org.openide.util.lookup.ServiceProvider; +import static org.netbeans.modules.apisupport.hints.Bundle.*; + +/** + * #207219: {@code DataObject.Registration} conversion. + * + */ +@ServiceProvider(service = Hinter.class) +public class DataObjectRegistrationHinter implements Hinter { + + private static final String LOADERS_FOLDER = "Loaders/"; + private static final String FACTORIES_FOLDER = "Factories"; + + @Override + public void process(final Context ctx) throws Exception { + final FileObject file = ctx.file(); + final Object instanceCreate = ctx.instanceAttribute(file); + if (instanceCreate == null) { + return; + } + + if (file.getPath().startsWith(LOADERS_FOLDER) && file.getPath().contains(FACTORIES_FOLDER)) { + ctx.addStandardAnnotationHint(new Callable() { + + @Override + public Void call() throws Exception { + if (!annotationsAvailable(ctx)) { + return null; + } + ctx.findAndModifyDeclaration(instanceCreate, new RegisterDataObject(ctx)); + return null; + } + }); + } + + + } + + @Messages("DataObjectRegistrationHinter.missing_dep=You must add a dependency on org.openide.loaders (7.36+) before using this fix.") + private boolean annotationsAvailable(Context ctx) { + if (ctx.canAccess(DataObject.Registration.class.getName()) && ctx.canAccess(DataObject.Registrations.class.getName())) { + return true; + } else { + DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(DataObjectRegistrationHinter_missing_dep(), NotifyDescriptor.WARNING_MESSAGE)); + return false; + } + } + + private static class RegisterDataObject implements Context.ModifyDeclarationTask { + + private static final String DATAOBJECT_REGISTRATION = "org.openide.loaders.DataObject.Registration"; + private static final String DATAOBJECT_REGISTRATIONS = "org.openide.loaders.DataObject.Registrations"; + private final Context ctx; + + private RegisterDataObject(Context ctx) { + this.ctx = ctx; + } + + @Override + public void run(WorkingCopy wc, Element declaration, ModifiersTree modifiers) throws Exception { + Map params = new HashMap(); + FileObject file = ctx.file(); + + String displayName = ctx.bundlevalue(file.getAttribute("literal:displayName"), declaration); + + if (displayName == null) { + displayName = "#TODO"; + } + // parameter of annotation + params.put("position", file.getAttribute("position")); + params.put("displayName", displayName); + params.put("iconBase", file.getAttribute("iconBase")); + params.put("mimeType", getMimeTypeFromPath(file.getPath())); + + ModifiersTree mt = ctx.addAnnotation(wc, modifiers, DATAOBJECT_REGISTRATION, DATAOBJECT_REGISTRATIONS, params); + ctx.delete(file); + wc.rewrite(modifiers, GeneratorUtilities.get(wc).importFQNs(mt)); + + } + + private String getMimeTypeFromPath(String path) { + String mimeType = path.replace(LOADERS_FOLDER, ""); + mimeType = mimeType.substring(0, mimeType.indexOf(FACTORIES_FOLDER) - 1);// -1 to remove last file separator + return mimeType; + } + } +} diff --git a/openide.loaders/src/org/netbeans/modules/openide/loaders/DataObjectFactoryProcessor.java b/openide.loaders/src/org/netbeans/modules/openide/loaders/DataObjectFactoryProcessor.java --- a/openide.loaders/src/org/netbeans/modules/openide/loaders/DataObjectFactoryProcessor.java +++ b/openide.loaders/src/org/netbeans/modules/openide/loaders/DataObjectFactoryProcessor.java @@ -68,113 +68,125 @@ */ @ServiceProvider(service = Processor.class) @SupportedSourceVersion(SourceVersion.RELEASE_6) -@SupportedAnnotationTypes("org.openide.loaders.DataObject.Registration") +@SupportedAnnotationTypes({"org.openide.loaders.DataObject.Registration", "org.openide.loaders.DataObject.Registrations"}) public class DataObjectFactoryProcessor extends LayerGeneratingProcessor { + @Override protected boolean handleProcess(Set annotations, RoundEnvironment roundEnv) throws LayerGenerationException { if (roundEnv.processingOver()) { return false; } - TypeMirror dataObjectType = type(DataObject.class); - TypeMirror fileObjectType = type(FileObject.class); - TypeMirror multiFileLoaderType = type(MultiFileLoader.class); - TypeMirror dataObjectFactoryType = type(DataObject.Factory.class); - for (Element e : roundEnv.getElementsAnnotatedWith(DataObject.Registration.class)) { DataObject.Registration dfr = e.getAnnotation(DataObject.Registration.class); if (dfr == null) { continue; } - LayerBuilder builder = layer(e); - //need class name to generate id and factory dataObjectClass parameter - String className = processingEnv.getElementUtils().getBinaryName((TypeElement)e).toString(); - String factoryId = className.replace(".", "-"); + process(e, dfr); + } + for (Element e : roundEnv.getElementsAnnotatedWith(DataObject.Registrations.class)) { + DataObject.Registrations dfrr = e.getAnnotation(DataObject.Registrations.class); + if (dfrr == null) { + continue; + } + for (DataObject.Registration t : dfrr.value()) { + process(e, t); + } + } + return true; + } - boolean useFactory = true; + // + private void process(Element e, DataObject.Registration dfr) throws LayerGenerationException { + TypeMirror dataObjectType = type(DataObject.class); + TypeMirror fileObjectType = type(FileObject.class); + TypeMirror multiFileLoaderType = type(MultiFileLoader.class); + TypeMirror dataObjectFactoryType = type(DataObject.Factory.class); + LayerBuilder builder = layer(e); + //need class name to generate id and factory dataObjectClass parameter + String className = processingEnv.getElementUtils().getBinaryName((TypeElement) e).toString(); + String factoryId = className.replace(".", "-"); - // test if enclosing element is of DataObject type; + boolean useFactory = true; - if (isAssignable(e.asType(), dataObjectType)) { - //attempt to use default factory - List ee = new LinkedList(); - // should be a public constructor with FileObject and MultiFileLoader as param - for (ExecutableElement element : ElementFilter.constructorsIn(e.getEnclosedElements())) { + // test if enclosing element is of DataObject type; - if ((element.getKind() == ElementKind.CONSTRUCTOR) && (element.getModifiers().contains(Modifier.PUBLIC))) { // found a public constructor ; - if ((element.getParameters().size() == 2) // parameters of constructor ok - && (isAssignable(element.getParameters().get(0).asType(), fileObjectType)) - && (isAssignable(element.getParameters().get(1).asType(), multiFileLoaderType))) { - ee.add(element); - } + if (isAssignable(e.asType(), dataObjectType)) { + //attempt to use default factory + List ee = new LinkedList(); + // should be a public constructor with FileObject and MultiFileLoader as param + for (ExecutableElement element : ElementFilter.constructorsIn(e.getEnclosedElements())) { + + if ((element.getKind() == ElementKind.CONSTRUCTOR) && (element.getModifiers().contains(Modifier.PUBLIC))) { // found a public constructor ; + if ((element.getParameters().size() == 2) // parameters of constructor ok + && (isAssignable(element.getParameters().get(0).asType(), fileObjectType)) + && (isAssignable(element.getParameters().get(1).asType(), multiFileLoaderType))) { + ee.add(element); } } - // nothing is found - if (ee.isEmpty()) { - throw new LayerGenerationException("DataObject subclass with @DataObject.Registration needs a public constructor with FileObject and MultiFileLoader parameters", e, processingEnv, dfr); // NOI18N - } else { - useFactory = true; - } + } + // nothing is found + if (ee.isEmpty()) { + throw new LayerGenerationException("DataObject subclass with @DataObject.Registration needs a public constructor with FileObject and MultiFileLoader parameters", e, processingEnv, dfr); // NOI18N + } else { + useFactory = true; + } - } else if (isAssignable(e.asType(), dataObjectFactoryType)) { - List ee = new LinkedList(); - for (ExecutableElement element : ElementFilter.constructorsIn(e.getEnclosedElements())) { - if ((element.getKind() == ElementKind.CONSTRUCTOR) && (element.getModifiers().contains(Modifier.PUBLIC))) { // found a public constructor ; - if ((element.getParameters().isEmpty())) {// parameters of constructor ok - ee.add(element); - } + } else if (isAssignable(e.asType(), dataObjectFactoryType)) { + List ee = new LinkedList(); + for (ExecutableElement element : ElementFilter.constructorsIn(e.getEnclosedElements())) { + if ((element.getKind() == ElementKind.CONSTRUCTOR) && (element.getModifiers().contains(Modifier.PUBLIC))) { // found a public constructor ; + if ((element.getParameters().isEmpty())) {// parameters of constructor ok + ee.add(element); } } - if (ee.isEmpty()) { - throw new LayerGenerationException("DataObject.Factory subclass with @DataObject.Registration needs a public default constructor", e, processingEnv, dfr); // NOI18N - } else { - useFactory = false; - factoryId = className.replace(".class", "").replace(".", "-"); - } + } + if (ee.isEmpty()) { + throw new LayerGenerationException("DataObject.Factory subclass with @DataObject.Registration needs a public default constructor", e, processingEnv, dfr); // NOI18N } else { - throw new LayerGenerationException("Usage @DataObject.Registration only on DataObject.Factory subclass or DataObject subclass", e, processingEnv, dfr); // NOI18N + useFactory = false; + factoryId = className.replace(".class", "").replace(".", "-"); + } + } else { + throw new LayerGenerationException("Usage @DataObject.Registration only on DataObject.Factory subclass or DataObject subclass", e, processingEnv, dfr); // NOI18N + } + + // check if mimeType annotation is set + if (dfr.mimeType().length == 0) { + throw new LayerGenerationException("@DataObject.Factory.Registration mimeTypes() cannot be null", e, processingEnv, dfr, "mimeTypes"); + } + // verify if all mimeType are valid + for (String aMimeType : dfr.mimeType()) { + if (aMimeType.isEmpty()) { + throw new LayerGenerationException("@DataObject.Factory.Registration mimeTypes() cannot have a empty mimeType", e, processingEnv, dfr, "mimeTypes"); + } + } + + + for (String aMimeType : dfr.mimeType()) { + LayerBuilder.File f = builder.file("Loaders/" + aMimeType + "/Factories/" + factoryId + ".instance"); + + // iconBase is optional but if set then shoud be in classpath + if (dfr.iconBase().length() > 0) { + builder.validateResource(dfr.iconBase(), e.getEnclosingElement(), dfr, "icon", true); + f.stringvalue("iconBase", dfr.iconBase()); + } + // position LayerBuilder + f.position(dfr.position()); + + if (!dfr.displayName().isEmpty()) { + f.bundlevalue("displayName", dfr.displayName(), dfr, "displayName"); } - // check if mimeType annotation is set - if (dfr.mimeType().length == 0) { - throw new LayerGenerationException("@DataObject.Factory.Registration mimeTypes() cannot be null", e, processingEnv, dfr, "mimeTypes"); + if (useFactory) { + f.methodvalue("instanceCreate", "org.openide.loaders.DataLoaderPool", "factory"); + f.stringvalue("dataObjectClass", className); + // if factory mimetype is needed otherwise not + f.stringvalue("mimeType", aMimeType); + } - // verify if all mimeType are valid - for (String aMimeType : dfr.mimeType()) { - if (aMimeType.isEmpty()) { - throw new LayerGenerationException("@DataObject.Factory.Registration mimeTypes() cannot have a empty mimeType", e, processingEnv, dfr, "mimeTypes"); - } - } - - - for (String aMimeType : dfr.mimeType()) { - LayerBuilder.File f = builder.file("Loaders/" + aMimeType + "/Factories/" + factoryId + ".instance"); - - // iconBase is optional but if set then shoud be in classpath - if (dfr.iconBase().length() > 0) { - builder.validateResource(dfr.iconBase(), e.getEnclosingElement(), dfr, "icon", true); - f.stringvalue("iconBase", dfr.iconBase()); - } - // position LayerBuilder - f.position(dfr.position()); - - if (!dfr.displayName().isEmpty()) { - f.bundlevalue("displayName", dfr.displayName(), dfr, "displayName"); - } - - if (useFactory) { - f.methodvalue("instanceCreate", "org.openide.loaders.DataLoaderPool", "factory"); - f.stringvalue("dataObjectClass", className); - // if factory mimetype is needed otherwise not - f.stringvalue("mimeType", aMimeType); - - } - f.write(); - } - + f.write(); } - - return true; } // reuse from Action Processor diff --git a/openide.loaders/src/org/openide/loaders/DataObject.java b/openide.loaders/src/org/openide/loaders/DataObject.java --- a/openide.loaders/src/org/openide/loaders/DataObject.java +++ b/openide.loaders/src/org/openide/loaders/DataObject.java @@ -1206,6 +1206,18 @@ int position() default Integer.MAX_VALUE; } + /** + * May be uses to allow multiple DataObject#Registration at one place. + * @since 7.36 + */ + @Retention(RetentionPolicy.SOURCE) + @Target({ElementType.TYPE}) + public static @interface Registrations { + + Registration[] value(); + } + + /** Registry of modified data objects. * The registry permits attaching of a change listener * to be informed when the count of modified objects changes. diff --git a/openide.loaders/test/unit/src/org/netbeans/modules/openide/loaders/DataObjectFactoryProcessorTest.java b/openide.loaders/test/unit/src/org/netbeans/modules/openide/loaders/DataObjectFactoryProcessorTest.java --- a/openide.loaders/test/unit/src/org/netbeans/modules/openide/loaders/DataObjectFactoryProcessorTest.java +++ b/openide.loaders/test/unit/src/org/netbeans/modules/openide/loaders/DataObjectFactoryProcessorTest.java @@ -45,9 +45,9 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; -import java.io.PrintStream; import org.netbeans.junit.NbTestCase; import org.netbeans.modules.openide.loaders.data.DoFPDataObject; +import org.netbeans.modules.openide.loaders.data.DoFPDataObjectMultiple; import org.openide.filesystems.FileLock; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; @@ -78,7 +78,6 @@ @Override protected void setUp() throws Exception { super.setUp(); - createMIMEs(); } // Several test for javac @@ -210,7 +209,19 @@ assertEquals("DataObjectClass found", DoFPDataObject.class.getName(), fo.getAttribute("dataObjectClass")); } + { + FileObject fo = FileUtil.getConfigFile( + "Loaders/text/testm3/Factories/" + DoFPDataObjectMultiple.class.getName().replace(".", "-") + ".instance"); + assertNotNull("File found", fo); + assertEquals("Position Ok", 4050, fo.getAttribute("position")); + assertEquals("Label Ok", "labeltestm1", fo.getAttribute("displayName")); + assertEquals("MimeOk", "text/testm3", fo.getAttribute("mimeType")); + Object icon = fo.getAttribute("iconBase"); + assertEquals("Icon found", "org/openide/loaders/unknown.gif", icon); + assertEquals("DataObjectClass found", DoFPDataObjectMultiple.class.getName(), fo.getAttribute("dataObjectClass")); + + } } // use external DoFP* class and their registration to test if dataobject return is good @@ -233,25 +244,15 @@ assertEquals("text/test3", fo.getMIMEType()); // XXX DoFPCustomLoader not loaded cannot assert for loader } + { + FileObject fo = createXmlFile("sdfsdf", ".ttm2"); + assertEquals("text/testm2", fo.getMIMEType()); + DataObject find = DataObject.find(fo); + assertEquals("DataLoader type", DoFPDataObjectMultiple.class, find.getClass()); + } } - // utility method inspired by FsMimeResolverTest - private void createMIMEs() throws Exception { -// create resolver to get tt1 extension resolve as test/test1 and do so for 3 different - FileObject resolver = FileUtil.createData(FileUtil.getConfigRoot(), "Services/MIMEResolver/resolver.xml"); - OutputStream os = resolver.getOutputStream(); - PrintStream ps = new PrintStream(os); - ps.println(""); - ps.println(""); - for (int i = 1; i < 4; i++) { - ps.println(" "); - ps.println(" "); - ps.println(" "); - ps.println(" "); - } - ps.println(""); - os.close(); - } + private FileObject createXmlFile(String content, String ext) throws Exception { FileObject file = FileUtil.createMemoryFileSystem().getRoot().createData("file" + ext); diff --git a/openide.loaders/test/unit/src/org/netbeans/modules/openide/loaders/data/DoFPDataObject.java b/openide.loaders/test/unit/src/org/netbeans/modules/openide/loaders/data/DoFPDataObjectMultiple.java copy from openide.loaders/test/unit/src/org/netbeans/modules/openide/loaders/data/DoFPDataObject.java copy to openide.loaders/test/unit/src/org/netbeans/modules/openide/loaders/data/DoFPDataObjectMultiple.java --- a/openide.loaders/test/unit/src/org/netbeans/modules/openide/loaders/data/DoFPDataObject.java +++ b/openide.loaders/test/unit/src/org/netbeans/modules/openide/loaders/data/DoFPDataObjectMultiple.java @@ -53,10 +53,12 @@ * @see DataObjectFactoryProcessorTest * @author Eric Barboni */ -@DataObject.Registration(mimeType = {"text/test1","text/test2"}, displayName = "labeltest", position = 3565, iconBase = "org/openide/loaders/unknown.gif") -public class DoFPDataObject extends DataObject { +@DataObject.Registrations({ + @DataObject.Registration(mimeType = {"text/testm1", "text/testm2"}, displayName = "labeltest", position = 3565, iconBase = "org/openide/loaders/unknown.gif"), + @DataObject.Registration(mimeType = {"text/testm3"}, displayName = "labeltestm1", position = 4050, iconBase = "org/openide/loaders/unknown.gif")}) +public class DoFPDataObjectMultiple extends DataObject { - public DoFPDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException { + public DoFPDataObjectMultiple(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException { super(pf, loader); } @@ -109,5 +111,4 @@ protected DataObject handleCreateFromTemplate(DataFolder df, String name) throws IOException { throw new UnsupportedOperationException("Not supported yet."); } - } diff --git a/openide.loaders/test/unit/src/org/netbeans/modules/openide/loaders/data/DoFPMIMEType.java b/openide.loaders/test/unit/src/org/netbeans/modules/openide/loaders/data/DoFPMIMEType.java new file mode 100644 --- /dev/null +++ b/openide.loaders/test/unit/src/org/netbeans/modules/openide/loaders/data/DoFPMIMEType.java @@ -0,0 +1,75 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 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 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.openide.loaders.data; + +import org.openide.filesystems.MIMEResolver; + +/** + * + * @author Eric Barboni + */ +public class DoFPMIMEType { + + @MIMEResolver.ExtensionRegistration(displayName = "tt1", extension = "tt1", mimeType = "text/test1",position=1000) + public void tt1() { + } + + @MIMEResolver.ExtensionRegistration(displayName = "tt2", extension = "tt2", mimeType = "text/test2",position=1001) + public void tt2() { + } + + @MIMEResolver.ExtensionRegistration(displayName = "tt3", extension = "tt3", mimeType = "text/test3",position=1002) + public void tt3() { + } + + @MIMEResolver.ExtensionRegistration(displayName = "ttm1", extension = "ttm1", mimeType = "text/testm1",position=1003) + public void ttm1() { + } + + @MIMEResolver.ExtensionRegistration(displayName = "ttm2", extension = "ttm2", mimeType = "text/testm2",position=1004) + public void ttm2() { + } + + @MIMEResolver.ExtensionRegistration(displayName = "ttm3", extension = "ttm3", mimeType = "text/testm3",position=1005) + public void ttm3() { + } +}