diff -r b3bbc4d76b98 settings/api/doc/org/netbeans/api/settings/package.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/settings/api/doc/org/netbeans/api/settings/package.html Tue Dec 23 13:03:05 2008 +0100 @@ -0,0 +1,53 @@ + + + + + + + org.netbeans.api.settings package + + + This package provides annotations to simplify use of + convertors. + + diff -r b3bbc4d76b98 settings/src/org/netbeans/api/settings/ConvertAsProperties.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/settings/src/org/netbeans/api/settings/ConvertAsProperties.java Tue Dec 23 13:03:05 2008 +0100 @@ -0,0 +1,47 @@ + +package org.netbeans.api.settings; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.Properties; + +/** Annotation to attach to object that is wishing to support conversion from + * and to {@link Properties}. More info about the format and protocol + * is available in separate document, + * here is the shortest possible howto: + *
+ * @ConvertAsProperties(dtd="-//Your Org//Your Setting//EN")
+ * public class YourObject {
+ *   public YourObject() {} // public constructor is a must
+ *   void readProperties(java.util.Properties p) {
+ *     // do the read
+ *   }
+ *   void writeProperties(java.util.Properties p) {
+ *     // handle the store
+ *   }
+ * }
+ * 
+ * + * @author Jaroslav Tulach + * @since 1.18 + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.TYPE) +public @interface ConvertAsProperties { + /** Public ID of the XML file that results in creation of the + * annotated type and to which the annotated type can be converted. + * @return public ID of the file's DTD + */ + String dtd(); + /** Shall every change in the object result in save? Or shall the + * object just be marked as dirty? + */ + boolean autostore() default true; + /** An array of properties that are ignored without marking the object + * as dirty or saving it. + * @return array of property names or "all" to ignore all properties + */ + String[] ignoreChanges() default {}; +} diff -r b3bbc4d76b98 settings/src/org/netbeans/modules/settings/convertors/ConvertorProcessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/settings/src/org/netbeans/modules/settings/convertors/ConvertorProcessor.java Tue Dec 23 13:03:05 2008 +0100 @@ -0,0 +1,262 @@ +/* + * 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 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. + */ + +package org.netbeans.modules.settings.convertors; + +import java.util.Set; +import javax.annotation.processing.Processor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.ElementFilter; +import org.netbeans.api.settings.ConvertAsProperties; +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.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +/** Processor to hide all the complexities of settings layer registration. + * + * @author Jaroslav Tulach + */ +@ServiceProvider(service=Processor.class) +@SupportedSourceVersion(SourceVersion.RELEASE_6) +@SupportedAnnotationTypes("org.netbeans.api.settings.ConvertAsProperties")//NOI18N +public class ConvertorProcessor extends LayerGeneratingProcessor { + + + @Override + protected boolean handleProcess( + Set annotations, + RoundEnvironment env + ) throws LayerGenerationException { + for (Element e : env.getElementsAnnotatedWith(ConvertAsProperties.class)) { + ConvertAsProperties reg = e.getAnnotation(ConvertAsProperties.class); + + String[] convElem = instantiableClassOrMethod(e, null); + final String dtd = reg.dtd(); + + String dtdCode = convertPublicId(dtd); + System.err.println("dtd: " + dtd); + System.err.println("mod: " + dtdCode); + + /* + + + + + + */ + layer(e).file("xml/entities" + dtdCode). + url("nbres:/org/netbeans/modules/settings/resources/properties-1_0.dtd"). + stringvalue("hint.originalPublicID", dtd).write(); + /* + + + + + + + + + */ + layer(e).file("xml/memory/" + convElem[0].replace('.', '/')). + stringvalue("settings.providerPath", "xml/lookups/" + dtdCode + ".instance"). + write(); + + /* + + + + + + + + + + */ + File f = layer(e).file("xml/lookups" + dtdCode + ".instance"). + methodvalue("instanceCreate", "org.netbeans.api.settings.Factory", "create"). + methodvalue("settings.convertor", "org.netbeans.api.settings.Factory", "properties"). + stringvalue("settings.instanceClass", convElem[0]). + stringvalue("settings.instanceOf", convElem[0]). + boolvalue("xmlproperties.preventStoring", !reg.autostore()); + commaSeparated(f, reg.ignoreChanges()).write(); + } + return false; + } + + /** Copied from FileEntityResolver from o.n.core module. + */ + @SuppressWarnings("fallthrough") + private static String convertPublicId (String publicID) { + char[] arr = publicID.toCharArray (); + + + int numberofslashes = 0; + int state = 0; + int write = 0; + OUT: for (int i = 0; i < arr.length; i++) { + char ch = arr[i]; + + switch (state) { + case 0: + // initial state + if (ch == '+' || ch == '-' || ch == 'I' || ch == 'S' || ch == 'O') { + // do not write that char + continue; + } + // switch to regular state + state = 1; + // fallthru + case 1: + // regular state expecting any character + if (ch == '/') { + state = 2; + if (++numberofslashes == 3) { + // last part of the ID, exit + break OUT; + } + arr[write++] = '/'; + continue; + } + break; + case 2: + // previous character was / + if (ch == '/') { + // ignore second / and write nothing + continue; + } + state = 1; + break; + } + + // write the char into the array + if (ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z' || ch >= '0' && ch <= '9') { + arr[write++] = ch; + } else { + arr[write++] = '_'; + } + } + + return new String (arr, 0, write); + } + + private static File commaSeparated(File f, String[] arr) { + if (arr.length == 0) { + return f; + } + + StringBuilder sb = new StringBuilder(); + String sep = ""; + for (String s : arr) { + sb.append(sep); + sb.append(s); + sep = ","; + } + return f.stringvalue("xmlproperties.ignoreChanges", sb.toString()); + } + + private String[] instantiableClassOrMethod(Element e, Class type) throws IllegalArgumentException, LayerGenerationException { + TypeMirror typeMirror = type != null ? processingEnv.getElementUtils().getTypeElement(type.getName().replace('$', '.')).asType() : null; + switch (e.getKind()) { + case CLASS: { + String clazz = processingEnv.getElementUtils().getBinaryName((TypeElement) e).toString(); + if (e.getModifiers().contains(Modifier.ABSTRACT)) { + throw new LayerGenerationException(clazz + " must not be abstract", e); + } + { + boolean hasDefaultCtor = false; + for (ExecutableElement constructor : ElementFilter.constructorsIn(e.getEnclosedElements())) { + if (constructor.getParameters().isEmpty()) { + hasDefaultCtor = true; + break; + } + } + if (!hasDefaultCtor) { + throw new LayerGenerationException(clazz + " must have a no-argument constructor", e); + } + } + if (typeMirror != null && !processingEnv.getTypeUtils().isAssignable(e.asType(), typeMirror)) { + throw new LayerGenerationException(clazz + " is not assignable to " + typeMirror, e); + } + return new String[] {clazz, null}; + } + case METHOD: { + String clazz = processingEnv.getElementUtils().getBinaryName((TypeElement) e.getEnclosingElement()).toString(); + String method = e.getSimpleName().toString(); + if (!e.getModifiers().contains(Modifier.STATIC)) { + throw new LayerGenerationException(clazz + "." + method + " must be static", e); + } + if (!((ExecutableElement) e).getParameters().isEmpty()) { + throw new LayerGenerationException(clazz + "." + method + " must not take arguments", e); + } + if (typeMirror != null && !processingEnv.getTypeUtils().isAssignable(((ExecutableElement) e).getReturnType(), typeMirror)) { + throw new LayerGenerationException(clazz + "." + method + " is not assignable to " + typeMirror, e); + } + return new String[] {clazz, method}; + } + default: + throw new IllegalArgumentException("Annotated element is not loadable as an instance: " + e); + } + } + + private static final class Parser extends DefaultHandler { + + @Override + public void notationDecl(String name, String publicId, String systemId) throws SAXException { + super.notationDecl(name, publicId, systemId); + System.err.println("pub: " + publicId); + System.err.println("sys: " + systemId); + System.err.println("name: " + name); + } + + } +} diff -r b3bbc4d76b98 settings/test/unit/src/org/netbeans/modules/settings/convertors/XMLPropertiesConvertorAnnotationTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/settings/test/unit/src/org/netbeans/modules/settings/convertors/XMLPropertiesConvertorAnnotationTest.java Tue Dec 23 13:03:05 2008 +0100 @@ -0,0 +1,93 @@ +/* + * 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 java.util.Properties; +import org.netbeans.junit.NbTestCase; + + +import org.netbeans.api.settings.ConvertAsProperties; +import org.netbeans.spi.settings.Convertor; + +import org.openide.filesystems.FileObject; +import org.openide.filesystems.Repository; + +/** Checks usage of annotation to assign XML properties convertor. + * + * @author Jaroslav Tulach + */ +public final class XMLPropertiesConvertorAnnotationTest extends NbTestCase { + /** Creates a new instance of XMLPropertiesConvertorTest */ + public XMLPropertiesConvertorAnnotationTest(String name) { + super(name); + } + + public void testReadWrite() throws Exception { + FileObject dtdFO = Repository.getDefault().getDefaultFileSystem(). + findResource("/xml/lookups/NetBeans_org_netbeans_modules_settings_xtest/DTD_XML_FooSetting_2_0.instance"); + assertNotNull("Provider not found", dtdFO); + Convertor c = XMLPropertiesConvertor.create(dtdFO); + AnnoFoo foo = new AnnoFoo(); + foo.setProperty1("xxx"); + CharArrayWriter caw = new CharArrayWriter(1024); + c.write(caw, foo); + caw.flush(); + caw.close(); + CharArrayReader car = new CharArrayReader(caw.toCharArray()); + Object obj = c.read(car); + assertEquals(foo, obj); + } + + @ConvertAsProperties( + dtd="-//NetBeans org.netbeans.modules.settings.xtest//DTD XML FooSetting 2.0//EN" + ) + public static class AnnoFoo extends FooSetting { + public void readProperties(Properties p) { + this.setProperty1(p.getProperty("p1")); + } + public void writeProperties(Properties p) { + p.setProperty("p1", this.getProperty1()); + } + } +}