You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by co...@apache.org on 2026/10/13 13:09:12 UTC
(cxf-xjc-utils) 01/01: Use public codemodel API in Bug986Plugin instead of reflection
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 <co...@apache.org>
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>