Giter VIP home page Giter VIP logo

xjc-documentation-annotation-plugin's Introduction

Autobuild Status

XJC plugin to bring XSD descriptions into annotations of generated classes

Habr article (in Russian) describing for what it and how to use.

Why that plugin born you may find at the end of readme, but now lets look what it does and how to use it!

What it does: <annotation><documentation> -> Java class annotations

Said we have this object described in XSD:

  <xs:complexType name="Customer">
    <xs:annotation>
      <xs:documentation>Пользователь</xs:documentation>
      </xs:annotation>
    <xs:sequence>
      <xs:element name="name" type="xs:string">
        <xs:annotation>
          <xs:documentation>Фамилия и имя</xs:documentation>
        </xs:annotation>
      </xs:element>
    </xs:sequence>
  </xs:complexType>

We run xjc like:

xjc -npa -no-header -d src/main/generated-java/ -p xsd.generated scheme.xsd

And got class like (getters, setters and any annotations omitted for simplicity):

public class Customer {
  @XmlElement(required = true)
  protected String name;
}

But in my case I want known how to class and fields was named in source file! So it what this plugin do!

So you get:

@XsdInfo(name = "Пользователь", xsdElementPart = "<complexType name=\"Customer\">\n  <complexContent>\n    <restriction base=\"{http://www.w3.org/2001/XMLSchema}anyType\">\n      <sequence>\n        <element name=\"name\" type=\"{http://www.w3.org/2001/XMLSchema}string\"/>\n      </sequence>\n    </restriction>\n  </complexContent>\n</complexType>")
public class Customer {

    @XmlElement(required = true)
    @XsdInfo(name = "Фамилия и имя")
    protected String name;
}

How to use

Manual call in commandline

If you want run it manually ensure jar class with plugin in run classpath and just add option -XPluginDescriptionAnnotation. F.e.:

xjc -npa -no-header -d src/main/generated-java/ -p xsd.generated -XPluginDescriptionAnnotation scheme.xsd

Call from Java/Groovy

  Driver.run(
    [
       '-XPluginDescriptionAnnotation'
        ,'-d', generatedClassesDir.absolutePath
        ,'-p', 'info.hubbitus.generated.test'
        ,'Example.xsd'
    ] as String[]
    ,new XJCListener() {...}
  )

See test XJCPluginDescriptionAnnotationTest for example.

Use from Gradle

With gradle-xjc-plugin:

plugins {
  id 'java'
  id 'org.unbroken-dome.xjc' version '1.4.1' // https://github.com/unbroken-dome/gradle-xjc-plugin
}

...

dependencies {
  xjcClasspath 'info.hubbitus:xjc-documentation-annotation-plugin:1.0'
}

// Results by default in `build/xjc/generated-sources`
xjcGenerate {
  source = fileTree('src/main/resources') { include '*.xsd' }
  packageLevelAnnotations = false
  targetPackage = 'info.hubbitus.xjc.plugin.example'
  extraArgs = [ '-XPluginDescriptionAnnotation' ]
}

Just run:

./gradlew xjcGenerate

Please look complete example in example-project-gradle directory - it have fully independent gradle project ot demonstrate how to use this plugin..

Development:

Build:

./gradlew jar

Run tests:

./gradlew test

Rationale (why it is born)

For our integration we have task load big amount of XSD files into MDM software (proprietary Unidata).

XJC is good tool for generate Java DTO classes from XSD specification. It was first part ow way. Then I got excellent reflections library and travers generated classes.

Problem was I was not be able name my model items with original annotations! Despite XJC place initial Javadoc which contains description and related part of XML element it have several problems:

  1. That only for class, and absent fo fields.
  2. Even for class I can't use javadoc in runtime

First approach to parse XSD for documentation on groovy works, but was very fragile and always require get updates and hacks.

I long time search way to bring such annotations into DTO classes itself to do not do work twice (generate classes and again parse XSD files manually). I did not found solution. And it is the reason born of that plugin.

Licensed under MIT

xjc-documentation-annotation-plugin's People

Contributors

hubbitus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

xjc-documentation-annotation-plugin's Issues

Build failed with Kotlin: multiple getters

My build.gradle.kts:

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
	id("org.springframework.boot") version "2.6.2"
	id("io.spring.dependency-management") version "1.0.11.RELEASE"
	id("org.unbroken-dome.xjc") version "1.4.1" // https://github.com/unbroken-dome/gradle-xjc-plugin
	war
	kotlin("jvm") version "1.6.10"
	kotlin("plugin.spring") version "1.6.10"
}

group = "com.bftcom.gosmail.terminal"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_1_8

repositories {
	mavenCentral()
}

dependencies {
	implementation("org.springframework.boot:spring-boot-starter-web")
	implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
	implementation("org.jetbrains.kotlin:kotlin-reflect")
	implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
	providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")
	testImplementation("org.springframework.boot:spring-boot-starter-test")

	xjcClasspath("info.hubbitus:xjc-documentation-annotation-plugin:1.1")
}

tasks.withType<KotlinCompile> {
	kotlinOptions {
		freeCompilerArgs = listOf("-Xjsr305=strict")
		jvmTarget = "1.8"
	}
}

tasks.withType<Test> {
	useJUnitPlatform()
}
tasks {
	// Results by default in `build/xjc/generated-sources`
	xjcGenerate {
		source = fileTree(mapOf("dir" to "src/main/resources/static/xsd", "include" to listOf("*.xsd")))
		packageLevelAnnotations = false
		targetPackage = "info.hubbitus.xjc.plugin.example"
		extraArgs = listOf("-XPluginDescriptionAnnotation")
	}
}

Error log:

gradlew xjcgenerate
> Task :xjcGenerate FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Some problems were found with the configuration of task ':xjcGenerate' (type 'XjcGenerate').
  - In plugin 'org.unbroken-dome.xjc' type 'org.unbrokendome.gradle.plugins.xjc.XjcGenerate' property 'contentForWildcard' has redundant getters: 'getContentForWildcard()' and 'isContentForWildcard()'.      

    Reason: Boolean property 'contentForWildcard' has both an `is` and a `get` getter.

    Possible solutions:
      1. Remove one of the getters.
      2. Annotate one of the getters with @Internal.

    Please refer to https://docs.gradle.org/7.3.2/userguide/validation_problems.html#redundant_getters for more details about this problem.
  - In plugin 'org.unbroken-dome.xjc' type 'org.unbrokendome.gradle.plugins.xjc.XjcGenerate' property 'enableIntrospection' has redundant getters: 'getEnableIntrospection()' and 'isEnableIntrospection()'.   

    Reason: Boolean property 'enableIntrospection' has both an `is` and a `get` getter.

    Possible solutions:
      1. Remove one of the getters.
      2. Annotate one of the getters with @Internal.

    Please refer to https://docs.gradle.org/7.3.2/userguide/validation_problems.html#redundant_getters for more details about this problem.
  - In plugin 'org.unbroken-dome.xjc' type 'org.unbrokendome.gradle.plugins.xjc.XjcGenerate' property 'extension' has redundant getters: 'getExtension()' and 'isExtension()'.

    Reason: Boolean property 'extension' has both an `is` and a `get` getter.

    Possible solutions:
      1. Remove one of the getters.
      2. Annotate one of the getters with @Internal.

    Please refer to https://docs.gradle.org/7.3.2/userguide/validation_problems.html#redundant_getters for more details about this problem.
  - In plugin 'org.unbroken-dome.xjc' type 'org.unbrokendome.gradle.plugins.xjc.XjcGenerate' property 'noFileHeader' has redundant getters: 'getNoFileHeader()' and 'isNoFileHeader()'.

    Reason: Boolean property 'noFileHeader' has both an `is` and a `get` getter.

    Possible solutions:
      1. Remove one of the getters.
      2. Annotate one of the getters with @Internal.

    Please refer to https://docs.gradle.org/7.3.2/userguide/validation_problems.html#redundant_getters for more details about this problem.
  - In plugin 'org.unbroken-dome.xjc' type 'org.unbrokendome.gradle.plugins.xjc.XjcGenerate' property 'packageLevelAnnotations' has redundant getters: 'getPackageLevelAnnotations()' and 'isPackageLevelAnnota
tions()'.

    Reason: Boolean property 'packageLevelAnnotations' has both an `is` and a `get` getter.

    Possible solutions:
      1. Remove one of the getters.
      2. Annotate one of the getters with @Internal.

    Please refer to https://docs.gradle.org/7.3.2/userguide/validation_problems.html#redundant_getters for more details about this problem.

NPE when <annotation> is not provided in XSD

First, an extract of stacktrace:

...
Caused by: java.lang.NullPointerException
    at info.hubbitus.XJCPluginDescriptionAnnotation.fieldGetDescriptionAnnotation (XJCPluginDescriptionAnnotation.java:199)
    at info.hubbitus.XJCPluginDescriptionAnnotation.access$200 (XJCPluginDescriptionAnnotation.java:80)
    at info.hubbitus.XJCPluginDescriptionAnnotation$2.<init> (XJCPluginDescriptionAnnotation.java:116)
    at info.hubbitus.XJCPluginDescriptionAnnotation.lambda$null$2 (XJCPluginDescriptionAnnotation.java:116)

Now, the problem happens, when in an XSD, there's no element (which is perfectly valid). This problem has actually two instances:

  • info.hubbitus.XJCPluginDescriptionAnnotation#fieldGetDescriptionAnnotation
  • info.hubbitus.XJCPluginDescriptionAnnotation#classInfoGetDescriptionAnnotation

I suggest to rewrite these two methods as follows, which fixes both instances of the problem:

	private static String classInfoGetDescriptionAnnotation(CClassInfo classInfo){
		XSAnnotation annotation = classInfo.getSchemaComponent().getAnnotation();
		return resolveDescription(annotation);
	}

	private static String fieldGetDescriptionAnnotation(CPropertyInfo propertyInfo){
		XSAnnotation annotation = resolveXSAnnotation(propertyInfo.getSchemaComponent());
		return resolveDescription(annotation);
	}

	private static String resolveDescription(XSAnnotation annotation) {
		String description = "";
		if (annotation != null && annotation.getAnnotation() != null) {
			description = ((BindInfo) annotation.getAnnotation()).getDocumentation();
			if (description == null) {
				description = "";
			}
		}
		return description.trim();
	}

	private static XSAnnotation resolveXSAnnotation(XSComponent schemaComponent) {
		XSAnnotation annotation;
		//<xs:complexType name="TDocumentRefer">
		//		<xs:attribute name="documentID" use="required">
		//			<xs:annotation>
		//				<xs:documentation>Идентификатор документа</xs:documentation>
		if (schemaComponent instanceof AttributeUseImpl) {
			annotation = ((AttributeUseImpl) schemaComponent).getDecl().getAnnotation();
		}
		// <xs:complexType name="TBasicInterdepStatement">
		//		<xs:element name="header" type="stCom:TInterdepStatementHeader" minOccurs="0">
		//				<xs:annotation>
		//					<xs:documentation>Заголовок заявления</xs:documentation>
		else if (schemaComponent instanceof ParticleImpl) {
			annotation = (((ParticleImpl) schemaComponent).getTerm()).getAnnotation();
		} else {
			throw new UnsupportedOperationException("Unsupported schema component class: " + schemaComponent.getClass().getName());
		}
		return annotation;
	}

multiple annotations issue

Hi,

I have a similar issue with java classes that were generated from xsd document.

In my problem, I have two documentation fields in an annotation.(like the one below) Using this solution worked but only the last documentation added to my java class. (the first was ignored). I tried to customize your solution but in fieldGetDescriptionAnnotation method, it looks like schemaComponent contains only one annotation, and that annotation contains one documentation or I am missing something.

Do you think this is possible to implement?

Thanks.

XSD example
<xs:element name="MsgId" type="CBPR_RestrictedFINXMax35Text">
xs:annotation
<xs:documentation source="Name" xml:lang="EN">MessageIdentification</xs:documentation>
<xs:documentation source="Definition" xml:lang="EN">Point to point reference, as assigned by the instructing party, and sent to the next party in the chain to unambiguously identify the message.
Usage: The instructing party has to make sure that MessageIdentification is unique per instructed party for a pre-agreed period.</xs:documentation>
</xs:annotation>
</xs:element>

java result
@xmlelement(name = "MsgId", required = true)
@XsdInfo(name = "Point to point reference, as assigned by the instructing party, and sent to the next party in the chain to unambiguously identify the message.\nUsage: The instructing party has to make sure that MessageIdentification is unique per instructed party for a pre-agreed period.")
protected String msgId;

Parameter for translate generated classes for russian xml

I have xml file with russian tags, after generating I have russian classes. I would like to have parameter for plugin, to convert cyrillic to latin for class and property names

Screenshot 2021-04-14 at 12 22 23

Screenshot 2021-04-14 at 12 22 40

[xml_xsd.zip](https://github.com/Hubbitus/xjc-documentation-annotation-plugin/files/6309816/xml_xsd.zip)

Unable to get multiple documentations

Hi,
I am using the plugin with jaxb2-maven-plugin.
I have an XSD with this kind of annotations :
<xs:element maxOccurs="1" minOccurs="0" name="Prty" type="Max16NumericText_T2S"> <xs:annotation> <xs:documentation source="Name" xml:lang="EN">Priority</xs:documentation> <xs:documentation source="Definition" xml:lang="EN">Relative indication of the processing precedence of the message over a (set of) Business Messages with assigned priorities.</xs:documentation> </xs:annotation> </xs:element>

The plugin only extracts the second documentation :
@Pattern(regexp = "[0-9]{1,16}") @XsdInfo(name = "Relative indication of the processing precedence of the message over a (set of) Business Messages with assigned priorities.") protected String prty;

Is there a workaround?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.