eclipse-ee4j/jaxb-ri

ElementOutlineImpl produces raw type and proposed fix

patrodyne opened this issue · 0 comments

ISSUE

The ElementOutlineImpl class produces a raw type when generating constructors. The source of the issue is a cast of the implementation type to the declared type at line 58:

Line 58

JExpression declaredType = JExpr.cast(cm.ref(Class.class),implType.boxify().dotclass());

For example, xjc example.xsd compiles this schema:

example.xsd

<xs:schema
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb"
    jaxb:version="3.0"
>

    <xs:element name="someJAXBElement" type="xs:string">
        <xs:annotation>
            <xs:appinfo>
                <jaxb:class name="SomeJAXBElement"/>
            </xs:appinfo>
        </xs:annotation>
    </xs:element>
</xs:schema>

into this generated class (with unsafe Class types):

SomeJAXBElement.java

package generated;

import javax.xml.namespace.QName;
import jakarta.xml.bind.JAXBElement;

public class SomeJAXBElement
    extends JAXBElement<String>
{

    protected static final QName NAME = new QName("", "someJAXBElement");

    public SomeJAXBElement(String value) {
        super(NAME, ((Class) String.class), null, value);
    }

    public SomeJAXBElement() {
        super(NAME, ((Class) String.class), null, null);
    }

}

The raw type occurs in the second argument of the calls to the super constructor.

PROPOSAL

The following application reproduces the issue from ElementOutlineImpl (see obj1) then repeats the generation of the cast of the implementation type to the declared type BUT narrows the Class to be parameterized by the implementation type (see obj2).

Demo: Main.java

package org.example.codemodel.demo02;

import com.sun.codemodel.*;

import java.io.File;
import java.io.Serializable;

/**
 * Example JCodeModel application.
 *
 * @see <a href="https://github.com/esfand/printit/blob/master/codemodel-tutorial.md">codemodel-tutorial</a>
 * @see <a href="https://sookocheff.com/post/java/generating-java-with-jcodemodel/">generating-java-with-jcodemodel</a>
 */
public class Main
{
    public static void main(String[] args) throws Exception
    {
        String targetPath = ( args.length > 0 ) ? args[0] : "generated-sources";

        // Instantiate a new JCodeModel
        JCodeModel cm = new JCodeModel();

        // Create a new package
        JPackage jp = cm._package("org.example.codemodel.demo02");

        // Create a new class
        JDefinedClass jc = jp._class("Demo02");

        // Implement Serializable
        jc._implements(Serializable.class);

        // Add constant serializable id
        jc.field(JMod.STATIC | JMod.FINAL, long.class, "serialVersionUID", JExpr.lit(1L));

        // Add default constructor
        JMethod dc = jc.constructor(JMod.PUBLIC);
        
        // Reference the implementation type for this demo.
        JType implType = cm.ref(String.class);
        
        // Add UNSAFE declared type
        dc.body().directStatement("// Class is a raw type.");
        dc.body().directStatement("// Generic type Class<T> should be parameterized.");
        JExpression ex1 = JExpr.cast(cm.ref(Class.class), implType.boxify().dotclass());
        dc.body().decl(JMod.FINAL, cm.ref(Object.class), "obj1", ex1);
        dc.body().directStatement("System.out.println(obj1);");

        // Add SAFE declared type
        dc.body().directStatement("// Class is parameterized.");
        JExpression ex2 = JExpr.cast(cm.ref(Class.class).narrow(implType), implType.boxify().dotclass());
        dc.body().decl(JMod.FINAL, cm.ref(Object.class), "obj2", ex2);
        dc.body().directStatement("System.out.println(obj2);");
        
        // Generate the code
        File targetFile = new File(targetPath);
        targetFile.mkdirs();
        cm.build(targetFile);
    }
}

The result is this CodeModel generated class where obj1 shows the raw type but obj2 shows the parameterized (generic) type.

Demo2.java

package org.example.codemodel.demo02;

import java.io.Serializable;

public class Demo02 implements Serializable
{
    final static long serialVersionUID = 1L;

    public Demo02() {
        // Class is a raw type.
        // Generic type Class<T> should be parameterized.
        final Object obj1 = ((Class) String.class);
        System.out.println(obj1);
        // Class is parameterized.
        final Object obj2 = ((Class<String> ) String.class);
        System.out.println(obj2);
    }
}

The build tree for the demonstration is:

Maven Layout

$ tree Demo02 
Demo02
├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   │   └── org
│   │   │       └── example
│   │   │           └── codemodel
│   │   │               └── demo02
│   │   │                   └── Main.java
│   │   └── resources
│   └── test
│       ├── java
│       └── resources
└── target
    ├── lib
    │   └── codemodel-2.6.jar
    ├── codemodel-demo02.jar
    └── generated-sources
        ├── annotations
        └── org
            └── example
                └── codemodel
                    └── demo02
                        └── Demo02.java

And the Maven POM is:

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/maven-v4_0_0.xsd"
>
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example.codemodel.demo02</groupId>
    <artifactId>demo02</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>
    <name>CodeModel Demo #02</name>

    <properties>
        <maven.compiler.release>11</maven.compiler.release>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <libdir>lib</libdir>
        <source>${basedir}/src</source>
        <target>${project.build.directory}</target>
    </properties>

    <dependencies>

        <!-- CodeModel -->
        <dependency>
            <groupId>com.sun.codemodel</groupId>
            <artifactId>codemodel</artifactId>
            <version>2.6</version>
        </dependency>

    </dependencies>

    <build>
        <defaultGoal>package</defaultGoal>
        <finalName>codemodel-demo02</finalName>

        <plugins>

            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
            </plugin>

            <plugin>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>3.5.0</version>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <includeScope>runtime</includeScope>
                            <outputDirectory>${target}/${libdir}</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.3.0</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>org.example.codemodel.demo02.Main</mainClass>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>${libdir}/</classpathPrefix>
                        </manifest>
                        <manifestEntries>
                            <Created-By>Example</Created-By>
                            <Built-By>Example</Built-By>
                            <JVM-Release>${maven.compiler.release}</JVM-Release>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>

        </plugins>
    </build>

</project>

The demonstration can be executed in the target path using:

$ mvn clean package
cd target
$ java -jar codemodel-demo02.jar 
org/example/codemodel/demo02/Demo02.java