JAXB binding schema of dtd file

核能气质少年 提交于 2019-12-07 10:03:23

问题


A noobie question. I am generating Java code from a dtd file (USPTO dtd). The DTD specifies the root element as follows:

<!ELEMENT us-patent-grant (doc-page+ | (us-bibliographic-data-grant , abstract* , drawings? , description , us-sequence-list-doc? , us-megatable-doc?,table-external-doc* , us-chemistry* , us-math* ,us-claim-statement , claims))>

When I run xjc with the following binding schema

    <?xml version="1.0"?>
    <xml-java-binding-schema version="1.0ea2">
       <element name="us-patent-grant" type="class" root="true"></element>
    </xml-java-binding-schema>

I see the following Java object generated

    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "", propOrder = {
        "docPageOrUsBibliographicDataGrantOrAbstractOrDrawingsOrDescriptionOrUsSequenceListDocOrUsMegatableDocOrTableExternalDocOrUsChemistryOrUsMathOrUsClaimStatementOrClaims"
    })
    @XmlRootElement(name = "us-patent-grant")
    public class UsPatentGrant {
       ....

    @XmlElements({
        @XmlElement(name = "doc-page", required = true, type = DocPage.class),
        @XmlElement(name = "us-bibliographic-data-grant", required = true, type = UsBibliographicDataGrant.class),
        @XmlElement(name = "abstract", required = true, type = Abstract.class),
        @XmlElement(name = "drawings", required = true, type = Drawings.class),
        @XmlElement(name = "description", required = true, type = Description.class),
        @XmlElement(name = "us-sequence-list-doc", required = true, type = UsSequenceListDoc.class),
        @XmlElement(name = "us-megatable-doc", required = true, type = UsMegatableDoc.class),
        @XmlElement(name = "table-external-doc", required = true, type = TableExternalDoc.class),
        @XmlElement(name = "us-chemistry", required = true, type = UsChemistry.class),
        @XmlElement(name = "us-math", required = true, type = UsMath.class),
        @XmlElement(name = "us-claim-statement", required = true, type = UsClaimStatement.class),
        @XmlElement(name = "claims", required = true, type = Claims.class)
    })
    protected List<Object> docPageOrUsBibliographicDataGrantOrAbstractOrDrawingsOrDescriptionOrUsSequenceListDocOrUsMegatableDocOrTableExternalDocOrUsChemistryOrUsMathOrUsClaimStatementOrClaims;

     .........

public List<Object> getDocPageOrUsBibliographicDataGrantOrAbstractOrDrawingsOrDescriptionOrUsSequenceListDocOrUsMegatableDocOrTableExternalDocOrUsChemistryOrUsMathOrUsClaimStatementOrClaims() {
    if (docPageOrUsBibliographicDataGrantOrAbstractOrDrawingsOrDescriptionOrUsSequenceListDocOrUsMegatableDocOrTableExternalDocOrUsChemistryOrUsMathOrUsClaimStatementOrClaims == null) {
        docPageOrUsBibliographicDataGrantOrAbstractOrDrawingsOrDescriptionOrUsSequenceListDocOrUsMegatableDocOrTableExternalDocOrUsChemistryOrUsMathOrUsClaimStatementOrClaims = new ArrayList<Object>();
    }
    return this.docPageOrUsBibliographicDataGrantOrAbstractOrDrawingsOrDescriptionOrUsSequenceListDocOrUsMegatableDocOrTableExternalDocOrUsChemistryOrUsMathOrUsClaimStatementOrClaims;
}

So my question is how can i change the name of the long getter

getDocPageOrUsBibliographicDataGrantOrAbstractOrDrawingsOrDescriptionOrUsSequenceListDocOrUsMegatableDocOrTableExternalDocOrUsChemistryOrUsMathOrUsClaimStatementOrClaims

in the binding schema?

thank you in advance.


回答1:


This isn't so much an answer as evidence for what you are suspecting.

In the JAXB/XJC source for com.sun.tools.xjc.reader.dtd.Element, in the bind() method, you can see this excerpt, iterating through the child elements of the Element:

if(b.isRepeated || b.elements.size()>1) {
    // collection
    StringBuilder name = new StringBuilder();
    for( Element e : b.elements ) {
        if(name.length()>0)
            name.append("Or");
        name.append(owner.model.getNameConverter().toPropertyName(e.name));
    }
    ...
} else {
    // single property
    String name = b.elements.iterator().next().name;
            String propName = owner.model.getNameConverter().toPropertyName(name);
            ...
}

That looks like it offers little opportunity to change the sequence of "Or" separators where there are multiple child elements.

You can see the bindInfo allowing custom names elsewhere in Element, but not near here. Unless someone else can spot it, the chances to rename things seem to end there. In short, the CElementPropertyInfo this outputs leads to BeanGenerator.fields, which lead to JDefinedClass.fields which are then output directly by JDefinedClass.declareBody().

As the XJC docs say, DTD support is still experimental...




回答2:


As @df778899 mentioned, if you looked at the below source code then nothing much can be done about it

CElementPropertyInfo p;
if(b.isRepeated || b.elements.size()>1) {
    // collection
    StringBuilder name = new StringBuilder();
    for( Element e : b.elements ) {
        if(name.length()>0)
            name.append("Or");
        name.append(owner.model.getNameConverter().toPropertyName(e.name));
    }
    p = new CElementPropertyInfo(name.toString(), REPEATED_ELEMENT, ID.NONE, null, null,null/*TODO*/, locator, !b.isOptional );
    for( Element e : b.elements ) {
        CClassInfo child = owner.getOrCreateElement(e.name).getClassInfo();
        assert child!=null; // we are requiring them to be classes.
        p.getTypes().add(new CTypeRef(child,new QName("",e.name),null,false,null));
    }

The CElementPropertyInfo object takes the name and the only way to fix is to use instrumentation. So I created a maven project

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.tarunlalwani</groupId>
    <artifactId>jxc-customizer</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
            <!-- https://mvnrepository.com/artifact/javassist/javassist -->
        <!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
            <dependency>
                <groupId>org.javassist</groupId>
                <artifactId>javassist</artifactId>
                <version>3.22.0-GA</version>
            </dependency>
        </dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <executions>
                <execution>
                    <id>copy-dependencies</id>
                    <phase>prepare-package</phase>
                    <goals>
                        <goal>copy-dependencies</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>
                            ${project.build.directory}/libs
                        </outputDirectory>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>2.4</version>
            <configuration>
                <archive>
                    <manifestEntries>
                        <Premain-Class>com.xjc.javaagent.SimpleAgent</Premain-Class>
                        <Boot-Class-Path>libs/javassist-3.22.0-GA.jar</Boot-Class-Path>
                        <Class-Path>libs/javassist-3.22.0-GA.jar</Class-Path>
                    </manifestEntries>
                    <manifest>
                        <classpathPrefix>libs/</classpathPrefix>

                    </manifest>
                </archive>
            </configuration>
        </plugin>

    </plugins>
</build>

</project>

jxccustomizer/src/main/java/com/xjc/javaagent/SimpleAgent.java

package com.xjc.javaagent;

import java.lang.instrument.Instrumentation;

public class SimpleAgent {

    public static void premain(String agentArgs,
                               Instrumentation instrumentation){

        System.out.println("Starting Agent");
        SimpleClassFileTransformer transformer =
                new SimpleClassFileTransformer();
        instrumentation.addTransformer(transformer);
    }
}

jxccustomizer/src/main/java/com/xjc/javaagent/SimpleClassFileTransformer.java

package com.xjc.javaagent;

import javassist.*;

import java.io.ByteArrayInputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;

public class SimpleClassFileTransformer implements
        ClassFileTransformer {

    public byte[] transform(ClassLoader loader,
                            String className, Class classBeingRedefined,
                            ProtectionDomain protectionDomain, byte[] classfileBuffer)
            throws IllegalClassFormatException {

        byte[] byteCode = classfileBuffer;
        if (className.endsWith("/CPropertyInfo")) {
            System.out.println("Loading class - " + className);
            try {
                ClassPool classPool = ClassPool.getDefault();
                CtClass ctClass = classPool.makeClass(
                        new ByteArrayInputStream(classfileBuffer));
                CtConstructor[] constructors = ctClass.getConstructors();
                for (CtConstructor c : constructors) {
                    c.insertBefore("{        String propFile = System.getProperty(\"XJC_REMAP\");\n" +
                            "        if (propFile != null)\n" +
                            "        {\n" +
                            "            System.out.println(\"External remap file provided\");\n" +
                            "            java.util.Properties props = new java.util.Properties();\n" +
                            "            try {\n" +
                            "                props.load(new java.io.FileInputStream(propFile));\n" +
                            "                java.util.Enumeration enums = props.propertyNames();\n" +
                            "                while (enums.hasMoreElements()) {\n" +
                            "                    String key = (String)enums.nextElement();\n" +
                            "                    String value = props.getProperty(key);\n" +
                            "                    try {\n" +
                            "                        System.out.println(\"Checking if \" + name + \" matches \" + key);\n" +
                            "                        java.util.regex.Pattern pat = java.util.regex.Pattern.compile(\"^\" + key +\"$\", java.util.regex.Pattern.CASE_INSENSITIVE);\n" +
                            "                        if (pat.matcher(name).find())\n" +
                            "                        {\n" +
                            "                            System.out.println(\"Replacing \" + name + \" with \" + value);\n" +
                            "                            name = value;\n" +
                            "                            break;\n" +
                            "                        }\n" +
                            "                    } finally {\n" +
                            "                        if (name == key) {\n" +
                            "                            System.out.println(\"Replacing \" + name + \" with \" + value);\n" +
                            "                            name = value;\n" +
                            "                        }\n" +
                            "                        break;\n" +
                            "                    }\n" +
                            "                }\n" +
                            "            } catch (java.io.IOException e) {\n" +
                            "                e.printStackTrace();\n" +
                            "            }\n" +
                            "        }}");
                }
                byteCode = ctClass.toBytecode();
                ctClass.detach();
                System.out.println("NO Exception occured");
            } catch (Throwable e) {
                System.out.println("Exception occurred");
                e.printStackTrace();
            }
        }

        return byteCode;
    }
}

The above class can load a properties file specified by -DXJC_REMAP=<filepath> which will be of below format

remap.properties

docPageOr.*=patentDocument

Now to test the solution. First need to generated the jar for the agent by running below command

mvn clean package

Next is to create some testing dtd

binding.xjb

<?xml version="1.0"?>
<xml-java-binding-schema>
    <options package="org"/>
    <element name="us-patent-grant" type="class" root="true"></element>
</xml-java-binding-schema>

testing.dtd

<?xml version="1.0" encoding="utf-8"?>
 <!-- DTD to write simple stories
      Made by Daniel K. Schneider / TECFA / University of Geneva
      VERSION 1.0
      30/10/2003 -->
<!ELEMENT us-patent-grant (doc-page+ | (us-bibliographic-data-grant , abstract* , drawings? , description , us-sequence-list-doc? , us-megatable-doc?,table-external-doc* , us-chemistry* , us-math* ,us-claim-statement , claims))>

Now to use this to run the agent with xjc

$ export _JAVA_OPTIONS="-javaagent:/Users/tarun.lalwani/Desktop/tarunlalwani.com/tarunlalwani/workshop/ub16/so/jaxb/jxccustomizer/target/jxc-customizer-1.0-SNAPSHOT.jar -DXJC_REMAP=/Users/tarun.lalwani/Desktop/tarunlalwani.com/tarunlalwani/workshop/ub16/so/jaxb/remap.properties"

$ xjc -b binding.xjb -dtd testing.dtd
Picked up _JAVA_OPTIONS: -javaagent:/Users/tarun.lalwani/Desktop/tarunlalwani.com/tarunlalwani/workshop/ub16/so/jaxb/jxccustomizer/target/jxc-customizer-1.0-SNAPSHOT.jar -DXJC_REMAP=/Users/tarun.lalwani/Desktop/tarunlalwani.com/tarunlalwani/workshop/ub16/so/jaxb/remap.properties
objc[33035]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/bin/xjc (0x10ed964c0) and /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x10eded4e0). One of the two will be used. Which one is undefined.
Starting Agent
Loading class 2 - com/sun/tools/internal/xjc/model/CPropertyInfo
inside try
Updating cons 1
Updating cons
NO Exception occured
parsing a schema...
External remap file provided
Checking if DocPageOrUsBibliographicDataGrantOrAbstractOrDrawingsOrDescriptionOrUsSequenceListDocOrUsMegatableDocOrTableExternalDocOrUsChemistryOrUsMathOrUsClaimStatementOrClaims matches docPageOr.*
Replacing DocPageOrUsBibliographicDataGrantOrAbstractOrDrawingsOrDescriptionOrUsSequenceListDocOrUsMegatableDocOrTableExternalDocOrUsChemistryOrUsMathOrUsClaimStatementOrClaims with patentDocument
compiling a schema...
org/Abstract.java
org/Claims.java
org/Description.java
org/DocPage.java
org/Drawings.java
org/ObjectFactory.java
org/TableExternalDoc.java
org/UsBibliographicDataGrant.java
org/UsChemistry.java
org/UsClaimStatement.java
org/UsMath.java
org/UsMegatableDoc.java
org/UsPatentGrant.java
org/UsSequenceListDoc.java

And now the content of generated file is as desired

The git repo for the above code is available on below link

https://github.com/tarunlalwani/xjc-java-agent-customization



来源:https://stackoverflow.com/questions/18667358/jaxb-binding-schema-of-dtd-file

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!