@ConvertAsProperties annotation
diff -r 3296fc6624df settings/nbproject/project.properties
--- a/settings/nbproject/project.properties Mon Jul 27 21:41:39 2009 +0200
+++ b/settings/nbproject/project.properties Tue Aug 04 10:23:11 2009 +0200
@@ -44,4 +44,4 @@
javadoc.apichanges=${basedir}/apichanges.xml
javadoc.arch=${basedir}/arch.xml
javadoc.main.page=org/netbeans/spi/settings/doc-files/api.html
-spec.version.base=1.19.0
+spec.version.base=1.20.0
diff -r 3296fc6624df settings/src/org/netbeans/api/settings/ConvertAsJavaBean.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/settings/src/org/netbeans/api/settings/ConvertAsJavaBean.java Tue Aug 04 10:23:11 2009 +0200
@@ -0,0 +1,81 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 1997-2007 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]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Sun Microsystems, Inc. Portions Copyright 2002-2003 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.
+ */
+
+package org.netbeans.api.settings;
+
+import java.beans.PropertyChangeListener;
+import java.beans.XMLDecoder;
+import java.beans.XMLEncoder;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Specifies the kind of persistence to use of the annotated class.
+ * Uses {@link XMLDecoder} and {@link XMLEncoder} to store and read
+ * values of the class (and by default also its subclasses).
+ *
+ * The format uses getters and setters of the bean and usually needs
+ * default constructor:
+ *
+ * @
ConvertAsJavaBean
+ * public class YourObject {
+ * public YourObject() {}
+ * public String getName();
+ * public void setName(String name);
+ * }
+ *
+ * If the bean supports {@link PropertyChangeListener} notifications and
+ * contains addPropertyChangeListener
method, the system
+ * starts to listen on existing objects and in case a property change
+ * is delivered, the new state of the object is persisted again.
+ *
+ * @author Jaroslav Tulach
+ * @since 1.20
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target(ElementType.TYPE)
+@Documented
+public @interface ConvertAsJavaBean {
+ /** Shall subclasses of this class be also converted as JavaBeans? */
+ boolean subclasses() default true;
+}
diff -r 3296fc6624df settings/src/org/netbeans/modules/settings/convertors/ConvertorProcessor.java
--- a/settings/src/org/netbeans/modules/settings/convertors/ConvertorProcessor.java Mon Jul 27 21:41:39 2009 +0200
+++ b/settings/src/org/netbeans/modules/settings/convertors/ConvertorProcessor.java Tue Aug 04 10:23:11 2009 +0200
@@ -54,7 +54,9 @@
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
+import org.netbeans.api.settings.ConvertAsJavaBean;
import org.netbeans.api.settings.ConvertAsProperties;
+import org.netbeans.modules.settings.Env;
import org.openide.filesystems.annotations.LayerBuilder.File;
import org.openide.filesystems.annotations.LayerGeneratingProcessor;
import org.openide.filesystems.annotations.LayerGenerationException;
@@ -66,7 +68,10 @@
*/
@ServiceProvider(service=Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_6)
-@SupportedAnnotationTypes("org.netbeans.api.settings.ConvertAsProperties")//NOI18N
+@SupportedAnnotationTypes({
+ "org.netbeans.api.settings.ConvertAsProperties", //NOI18N
+ "org.netbeans.api.settings.ConvertAsJavaBean" //NOI18N
+})
public class ConvertorProcessor extends LayerGeneratingProcessor {
@@ -82,7 +87,7 @@
for (Element e : env.getElementsAnnotatedWith(ConvertAsProperties.class)) {
ConvertAsProperties reg = e.getAnnotation(ConvertAsProperties.class);
- String convElem = instantiableClassOrMethod(e);
+ String convElem = instantiableClassOrMethod(e, true);
final String dtd = reg.dtd();
String dtdCode = convertPublicId(dtd);
@@ -131,6 +136,18 @@
boolvalue("xmlproperties.preventStoring", !reg.autostore());
commaSeparated(f, reg.ignoreChanges()).write();
}
+
+
+ for (Element e : env.getElementsAnnotatedWith(ConvertAsJavaBean.class)) {
+ ConvertAsJavaBean reg = e.getAnnotation(ConvertAsJavaBean.class);
+ String convElem = instantiableClassOrMethod(e, false);
+ File f = layer(e).file("xml/memory/" + convElem.replace('.', '/'));
+ f.stringvalue("settings.providerPath", "xml/lookups/NetBeans/DTD_XML_beans_1_0.instance");
+ if (reg.subclasses()) {
+ f.boolvalue(Env.EA_SUBCLASSES, true);
+ }
+ f.write();
+ }
return true;
}
@@ -205,7 +222,7 @@
return f.stringvalue("xmlproperties.ignoreChanges", sb.toString());
}
- private String instantiableClassOrMethod(Element e) throws IllegalArgumentException, LayerGenerationException {
+ private String instantiableClassOrMethod(Element e, boolean checkMethods) throws IllegalArgumentException, LayerGenerationException {
switch (e.getKind()) {
case CLASS: {
String clazz = processingEnv.getElementUtils().getBinaryName((TypeElement) e).toString();
@@ -224,9 +241,9 @@
throw new LayerGenerationException(clazz + " must have a no-argument constructor", e);
}
}
- TypeMirror propType;
- propType = processingEnv.getElementUtils().getTypeElement("java.util.Properties").asType();
- {
+ if (checkMethods) {
+ TypeMirror propType;
+ propType = processingEnv.getElementUtils().getTypeElement("java.util.Properties").asType();
boolean hasRead = false;
boolean hasWrite = false;
for (ExecutableElement m : ElementFilter.methodsIn(e.getEnclosedElements())) {
@@ -256,7 +273,7 @@
return clazz;
}
default:
- throw new IllegalArgumentException("Annotated element is not loadable as an instance: " + e);
+ throw new LayerGenerationException("Annotated element is not loadable as an instance: " + e);
}
}
}
diff -r 3296fc6624df settings/src/org/netbeans/modules/settings/convertors/XMLBeanConvertor.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/settings/src/org/netbeans/modules/settings/convertors/XMLBeanConvertor.java Tue Aug 04 10:23:11 2009 +0200
@@ -0,0 +1,179 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 1997-2007 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]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Sun Microsystems, Inc. Portions Copyright 2002-2003 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.
+ */
+
+package org.netbeans.modules.settings.convertors;
+
+import java.beans.PropertyChangeListener;
+import java.beans.XMLDecoder;
+import java.beans.XMLEncoder;
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.CharBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+import org.netbeans.spi.settings.Convertor;
+import org.netbeans.spi.settings.Saver;
+
+import org.openide.util.Exceptions;
+import org.openide.util.io.ReaderInputStream;
+
+/** Convertor using {@link java.beans.XMLEncoder} and
+ * {@link java.beans.XMLDecoder}.
+ *
+ * @author Jaroslav Tulach
+ */
+public final class XMLBeanConvertor extends Convertor implements PropertyChangeListener {
+ /** create convertor instance; should be used in module layers
+ * @param providerFO provider file object
+ */
+ public static Convertor create() {
+ return new XMLBeanConvertor();
+ }
+
+ public XMLBeanConvertor() {
+ }
+
+ public Object read(java.io.Reader r) throws IOException, ClassNotFoundException {
+ java.io.
+ BufferedReader buf = new BufferedReader(r, 4096);
+ CharBuffer arr = CharBuffer.allocate(2048);
+ buf.mark(arr.capacity());
+ buf.read(arr);
+ arr.flip();
+
+ Matcher m = Pattern.compile("\n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -r 3296fc6624df settings/src/org/netbeans/modules/settings/resources/mf-layer.xml
--- a/settings/src/org/netbeans/modules/settings/resources/mf-layer.xml Mon Jul 27 21:41:39 2009 +0200
+++ b/settings/src/org/netbeans/modules/settings/resources/mf-layer.xml Tue Aug 04 10:23:11 2009 +0200
@@ -50,6 +50,12 @@
+
+
+
+
+
+
@@ -68,6 +74,9 @@
+
+
+
diff -r 3296fc6624df settings/test/unit/src/org/netbeans/modules/settings/convertors/ConvertAsBeanTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/settings/test/unit/src/org/netbeans/modules/settings/convertors/ConvertAsBeanTest.java Tue Aug 04 10:23:11 2009 +0200
@@ -0,0 +1,226 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 1997-2007 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]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Sun Microsystems, Inc. Portions Copyright 2002 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.
+ */
+
+package org.netbeans.modules.settings.convertors;
+
+import java.io.*;
+
+import org.netbeans.api.settings.ConvertAsJavaBean;
+import org.netbeans.junit.NbTestCase;
+
+
+
+import org.openide.cookies.InstanceCookie;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.loaders.DataFolder;
+import org.openide.loaders.DataObject;
+import org.openide.loaders.InstanceDataObject;
+import org.openide.modules.ModuleInfo;
+import org.openide.util.Lookup;
+import org.openide.util.test.AnnotationProcessorTestUtils;
+
+/** Checks usage of annotation to assign XML properties convertor.
+ *
+ * @author Jaroslav Tulach
+ */
+public final class ConvertAsBeanTest extends NbTestCase {
+ /** Creates a new instance of XMLPropertiesConvertorTest */
+ public ConvertAsBeanTest(String name) {
+ super(name);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ clearWorkDir();
+ Lookup.getDefault().lookup(ModuleInfo.class);
+ }
+
+ public void testReadWrite() throws Exception {
+ AnnoFoo foo = new AnnoFoo();
+ foo.setName("xxx");
+
+ DataFolder test = DataFolder.findFolder(FileUtil.getConfigRoot());
+ DataObject obj = InstanceDataObject.create(test, null, foo, null);
+ final FileObject pf = obj.getPrimaryFile();
+ final String content = pf.asText();
+ if (content.indexOf("xxx") == -1) {
+ fail(content);
+ }
+ obj.setValid(false);
+ DataObject newObj = DataObject.find(pf);
+ if (newObj == obj) {
+ fail("Strange, objects shall differ");
+ }
+ InstanceCookie ic = newObj.getLookup().lookup(InstanceCookie.class);
+ assertNotNull("Instance cookie found", ic);
+
+ Object read = ic.instanceCreate();
+ assertNotNull("Instance created", read);
+ assertEquals("Correct class", AnnoFoo.class, read.getClass());
+ AnnoFoo readFoo = (AnnoFoo)read;
+ assertEquals("property changed", "xxx", readFoo.getName());
+ }
+
+ public void testReadWriteOnSubclass() throws Exception {
+ HooFoo foo = new HooFoo();
+ foo.setName("xxx");
+
+ DataFolder test = DataFolder.findFolder(FileUtil.getConfigRoot());
+ DataObject obj = InstanceDataObject.create(test, null, foo, null);
+ final FileObject pf = obj.getPrimaryFile();
+ final String content = pf.asText();
+ if (content.indexOf("xxx") == -1) {
+ fail(content);
+ }
+ obj.setValid(false);
+ DataObject newObj = DataObject.find(pf);
+ if (newObj == obj) {
+ fail("Strange, objects shall differ");
+ }
+ InstanceCookie ic = newObj.getLookup().lookup(InstanceCookie.class);
+ assertNotNull("Instance cookie found", ic);
+
+ Object read = ic.instanceCreate();
+ assertNotNull("Instance created", read);
+ assertEquals("Correct class", HooFoo.class, read.getClass());
+ HooFoo readFoo = (HooFoo)read;
+ assertEquals("property changed", "xxx", readFoo.getName());
+ }
+
+ @ConvertAsJavaBean(
+ )
+ public static class AnnoFoo extends Object {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+ } // end of AnnoFoo
+
+ public static class HooFoo extends AnnoFoo {
+ private int count;
+
+ public int getCount() {
+ return count;
+ }
+
+ public void setCount(int count) {
+ this.count = count;
+ }
+ } // end of HooFoo
+
+ @ConvertAsJavaBean(
+ subclasses=false
+ )
+ public static class JuuFoo extends Object {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+ } // end of JuuFoo
+
+ public static class NotFoo extends JuuFoo {
+ private int count;
+
+ public int getCount() {
+ return count;
+ }
+
+ public void setCount(int count) {
+ this.count = count;
+ }
+ } // end of NotFoo
+ public void testReadWriteNotForSubclasses() throws Exception {
+ NotFoo foo = new NotFoo();
+ foo.setName("xxx");
+
+ DataFolder test = DataFolder.findFolder(FileUtil.getConfigRoot());
+ try {
+ DataObject obj = InstanceDataObject.create(test, null, foo, null);
+ final FileObject pf = obj.getPrimaryFile();
+ final String content = pf.asText();
+ fail(content);
+ } catch (NotSerializableException ex) {
+ // OK
+ }
+ }
+
+ public void testVerifyHaveDefaultConstructor() throws Exception {
+ AnnotationProcessorTestUtils.makeSource(getWorkDir(), "x.y.Kuk",
+ "import org.netbeans.api.settings.ConvertAsJavaBean;\n" +
+ "@ConvertAsJavaBean()\n" +
+ "public class Kuk {\n" +
+ " public Kuk(int i) {}\n" +
+ "}\n"
+ );
+ ByteArrayOutputStream err = new ByteArrayOutputStream();
+ boolean res = AnnotationProcessorTestUtils.runJavac(getWorkDir(), null, getWorkDir(), null, err);
+ assertFalse("Should fail", res);
+ if (err.toString().indexOf("x.y.Kuk must have a no-argument constructor") == -1) {
+ fail("Wrong error message:\n" + err.toString());
+ }
+ }
+ public void testInterfacesCannotBeAnnotated() throws Exception {
+ AnnotationProcessorTestUtils.makeSource(getWorkDir(), "x.y.Kuk",
+ "import org.netbeans.api.settings.ConvertAsJavaBean;\n" +
+ "@ConvertAsJavaBean()\n" +
+ "public interface Kuk {\n" +
+ " public int buk(int i);\n" +
+ "}\n"
+ );
+ ByteArrayOutputStream err = new ByteArrayOutputStream();
+ boolean res = AnnotationProcessorTestUtils.runJavac(getWorkDir(), null, getWorkDir(), null, err);
+ assertFalse("Should fail", res);
+ if (err.toString().indexOf("is not loadable") == -1) {
+ fail("Wrong error message:\n" + err.toString());
+ }
+ }
+}