This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch coheigea/bug986 in repository https://gitbox.apache.org/repos/asf/cxf-xjc-utils.git
commit 68f29240090cb1f829901c133aa08298dd5ed969 Author: Colm O hEigeartaigh <[email protected]> AuthorDate: Tue Oct 13 14:08:36 2026 +0100 Use public codemodel API in Bug986Plugin instead of reflection --- bug986/pom.xml | 5 + .../org/apache/cxf/xjc/bug986/Bug986Plugin.java | 65 ++--------- .../apache/cxf/xjc/bug986/Bug986PluginTest.java | 127 +++++++++++++++++++++ bug986/src/test/resources/bug986.xsd | 54 +++++++++ 4 files changed, 194 insertions(+), 57 deletions(-) diff --git a/bug986/pom.xml b/bug986/pom.xml index d0b9446..3091ca9 100644 --- a/bug986/pom.xml +++ b/bug986/pom.xml @@ -41,6 +41,11 @@ <artifactId>jaxb-runtime</artifactId> <scope>provided</scope> </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> </dependencies> </project> diff --git a/bug986/src/main/java/org/apache/cxf/xjc/bug986/Bug986Plugin.java b/bug986/src/main/java/org/apache/cxf/xjc/bug986/Bug986Plugin.java index 4040707..a30cc43 100644 --- a/bug986/src/main/java/org/apache/cxf/xjc/bug986/Bug986Plugin.java +++ b/bug986/src/main/java/org/apache/cxf/xjc/bug986/Bug986Plugin.java @@ -21,10 +21,7 @@ package org.apache.cxf.xjc.bug986; import java.io.StringWriter; -import java.lang.reflect.Field; import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.logging.Logger; @@ -34,10 +31,8 @@ import org.xml.sax.ErrorHandler; import com.sun.codemodel.JAnnotationUse; import com.sun.codemodel.JAnnotationValue; -import com.sun.codemodel.JClass; import com.sun.codemodel.JFieldVar; import com.sun.codemodel.JFormatter; -import com.sun.codemodel.JVar; import com.sun.tools.xjc.Options; import com.sun.tools.xjc.Plugin; import com.sun.tools.xjc.outline.ClassOutline; @@ -75,11 +70,13 @@ public class Bug986Plugin { for (ClassOutline classOutline : outline.getClasses()) { Map<String, JFieldVar> fields = classOutline.implClass.fields(); for (JFieldVar field : fields.values()) { - Collection<JAnnotationUse> annotations = getAnnotations(field); - List<JAnnotationUse> toRemove = new ArrayList<JAnnotationUse>(); - for (JAnnotationUse j : annotations) { - if (XmlSchemaType.class.getName().equals(getAnnotationClass(j).fullName())) { - JAnnotationValue st = getAnnotationMember(j, "name"); + List<JAnnotationUse> toRemove = new ArrayList<>(); + for (JAnnotationUse j : field.annotations()) { + if (XmlSchemaType.class.getName().equals(j.getAnnotationClass().fullName())) { + JAnnotationValue st = j.getAnnotationMembers().get("name"); + if (st == null) { + continue; + } StringWriter sw = new StringWriter(); st.generate(new JFormatter(sw)); if ("\"anySimpleType\"".equals(sw.toString())) { @@ -96,56 +93,10 @@ public class Bug986Plugin { } } for (JAnnotationUse j : toRemove) { - annotations.remove(j); + field.removeAnnotation(j); } } } return true; } - - private JAnnotationValue getAnnotationMember(JAnnotationUse ju, String name) { - try { - Field f = JAnnotationUse.class.getDeclaredField("memberValues"); - f.setAccessible(true); - @SuppressWarnings("unchecked") - Map<String, JAnnotationValue> memberValues = (Map<String, JAnnotationValue>)f.get(ju); - if (memberValues == null) { - return null; - } - return memberValues.get(name); - } catch (Throwable t) { - //ignore for now - t.printStackTrace(); - } - return null; - } - - private JClass getAnnotationClass(JAnnotationUse ju) { - try { - Field f = JAnnotationUse.class.getDeclaredField("clazz"); - f.setAccessible(true); - return (JClass)f.get(ju); - } catch (Throwable t) { - //ignore for now - t.printStackTrace(); - } - return null; - } - - @SuppressWarnings("unchecked") - private List<JAnnotationUse> getAnnotations(JFieldVar field) { - try { - Field f = JVar.class.getDeclaredField("annotations"); - f.setAccessible(true); - List<?> anns = (List<?>)f.get(field); - if (anns == null) { - anns = Collections.emptyList(); - } - return (List<JAnnotationUse>)anns; - } catch (Throwable t) { - //ignore for now - t.printStackTrace(); - } - return Collections.emptyList(); - } } diff --git a/bug986/src/test/java/org/apache/cxf/xjc/bug986/Bug986PluginTest.java b/bug986/src/test/java/org/apache/cxf/xjc/bug986/Bug986PluginTest.java new file mode 100644 index 0000000..8ebaea2 --- /dev/null +++ b/bug986/src/test/java/org/apache/cxf/xjc/bug986/Bug986PluginTest.java @@ -0,0 +1,127 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.xjc.bug986; + +import java.io.File; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.stream.Stream; + +import com.sun.tools.xjc.Driver; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class Bug986PluginTest { + + private static final String ANY_SIMPLE_TYPE = "@XmlSchemaType(name = \"anySimpleType\")"; + + @Rule + public TemporaryFolder tmp = new TemporaryFolder(); + + @Test + public void testWithoutPlugin() throws Exception { + String src = generate(false); + Assert.assertTrue(annotationsOf(src, "protected Integer intAttr;").contains(ANY_SIMPLE_TYPE)); + Assert.assertTrue(annotationsOf(src, "protected String stringAttr;").contains(ANY_SIMPLE_TYPE)); + } + + @Test + public void testRemovesAnySimpleTypeFromNonStringFields() throws Exception { + String src = generate(true); + Assert.assertFalse(annotationsOf(src, "protected Integer intAttr;").contains(ANY_SIMPLE_TYPE)); + Assert.assertTrue(annotationsOf(src, "protected String stringAttr;").contains(ANY_SIMPLE_TYPE)); + } + + /* + * Run XJC on the module path, where the codemodel module does not open its + * packages to the plugin, so reflection on codemodel internals fails. + */ + @Test + public void testOnModulePath() throws Exception { + List<String> modulePath = new ArrayList<>(); + for (String e : System.getProperty("java.class.path").split(File.pathSeparator)) { + if (e.endsWith(".jar")) { + modulePath.add(e); + } + } + // the plugin's classes as an automatic module + modulePath.add(jar(Paths.get(Bug986Plugin.class.getProtectionDomain().getCodeSource().getLocation().toURI()), + tmp.newFile("cxf-xjc-bug986.jar").toPath())); + File out = tmp.newFolder(); + String schema = new File(getClass().getResource("/bug986.xsd").toURI()).getAbsolutePath(); + Process p = new ProcessBuilder( + Paths.get(System.getProperty("java.home"), "bin", "java").toString(), + "--module-path", String.join(File.pathSeparator, modulePath), + "--add-modules", "ALL-MODULE-PATH", + "com.sun.tools.xjc.XJCFacade", + "-extension", "-Xbug986", "-d", out.getAbsolutePath(), schema) + .inheritIO().start(); + Assert.assertTrue("XJC did not finish", p.waitFor(2, TimeUnit.MINUTES)); + Assert.assertEquals(0, p.exitValue()); + + File holder = new File(out, "org/apache/cxf/bug986/Holder.java"); + String src = new String(Files.readAllBytes(holder.toPath()), StandardCharsets.UTF_8); + Assert.assertFalse(annotationsOf(src, "protected Integer intAttr;").contains(ANY_SIMPLE_TYPE)); + Assert.assertTrue(annotationsOf(src, "protected String stringAttr;").contains(ANY_SIMPLE_TYPE)); + } + + private static String jar(Path classes, Path jar) throws Exception { + try (OutputStream os = Files.newOutputStream(jar); + JarOutputStream jos = new JarOutputStream(os); + Stream<Path> files = Files.walk(classes)) { + for (Path f : (Iterable<Path>) files.filter(Files::isRegularFile)::iterator) { + jos.putNextEntry(new JarEntry(classes.relativize(f).toString().replace(File.separatorChar, '/'))); + Files.copy(f, jos); + jos.closeEntry(); + } + } + return jar.toString(); + } + + private String generate(boolean withPlugin) throws Exception { + File out = tmp.newFolder(); + String schema = new File(getClass().getResource("/bug986.xsd").toURI()).getAbsolutePath(); + String[] args = withPlugin + ? new String[] {"-extension", "-Xbug986", "-d", out.getAbsolutePath(), schema} + : new String[] {"-extension", "-d", out.getAbsolutePath(), schema}; + Assert.assertEquals(0, Driver.run(args, System.out, System.out)); + File holder = new File(out, "org/apache/cxf/bug986/Holder.java"); + return new String(Files.readAllBytes(holder.toPath()), StandardCharsets.UTF_8); + } + + // the annotations written between the previous declaration and the given field + private static String annotationsOf(String src, String field) { + int idx = src.indexOf(field); + Assert.assertTrue(field + " not generated", idx != -1); + int start = Math.max(src.lastIndexOf(';', idx), src.lastIndexOf('{', idx)); + return src.substring(start + 1, idx); + } +} diff --git a/bug986/src/test/resources/bug986.xsd b/bug986/src/test/resources/bug986.xsd new file mode 100644 index 0000000..9621569 --- /dev/null +++ b/bug986/src/test/resources/bug986.xsd @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--> +<!-- + Mapping xs:anySimpleType to a non-String type leaves + @XmlSchemaType(name = "anySimpleType") on the generated attribute field. +--> +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb" + xmlns:tns="http://cxf.apache.org/bug986" targetNamespace="http://cxf.apache.org/bug986" + elementFormDefault="qualified" jaxb:version="3.0"> + + <xs:annotation> + <xs:appinfo> + <jaxb:globalBindings> + <jaxb:javaType name="java.lang.Integer" xmlType="xs:anySimpleType" + parseMethod="java.lang.Integer.valueOf" printMethod="java.lang.String.valueOf"/> + </jaxb:globalBindings> + </xs:appinfo> + </xs:annotation> + + <xs:complexType name="holder"> + <xs:attribute name="intAttr" type="xs:anySimpleType"/> + <xs:attribute name="stringAttr" type="xs:anySimpleType"> + <xs:annotation> + <xs:appinfo> + <jaxb:property> + <jaxb:baseType> + <jaxb:javaType name="java.lang.String" + parseMethod="jakarta.xml.bind.DatatypeConverter.parseString" + printMethod="jakarta.xml.bind.DatatypeConverter.printString"/> + </jaxb:baseType> + </jaxb:property> + </xs:appinfo> + </xs:annotation> + </xs:attribute> + </xs:complexType> + +</xs:schema>
