package com.sun.xml.bind.v2.schemagen;
import java.lang.reflect.Field;
import at.ac.tuwien.infosys.jaxb.XmlSchemaEnhancer;
import com.sun.xml.bind.v2.model.core.PropertyInfo;
import com.sun.xml.bind.v2.schemagen.xmlschema.ContentModelContainer;
import com.sun.xml.bind.v2.schemagen.xmlschema.Particle;
/**
* This class is used to intercept the schema generation for group
* types (<sequence>, <choice>) in order to add <annotation> elements
* directly into the groups -- instead of putting the <annotation>
* into the children of the group which is the default case. See
* class javax.xml.bind.annotation.AnnotationLocation for more info.
*
* @author Waldemar Hummer (hummer@infosys.tuwien.ac.at)
* @since JAXB-Facets version 1.0.11
*/
public class TreeWrapper<T,C> extends Tree {
private Tree wrapped;
private PropertyInfo<T,C> elementInfo;
private TreeWrapper(Tree t, PropertyInfo<T,C> elementInfo) {
this.wrapped = t;
this.elementInfo = elementInfo;
}
public static <T,C> TreeWrapper<T,C> wrap(Tree t, PropertyInfo<T,C> elementInfo) {
return new TreeWrapper<T,C>(t, elementInfo);
}
@Override
protected void write(ContentModelContainer parent, boolean isOptional,
boolean repeated) {
Class<?> clazz = wrapped.getClass();
/* special treatment for Groups, in particular CHOICE groups.
* If @Annotation.location() == AnnotationLocation.OUTSIDE_ELEMENT,
* we want to be able to write <xsd:annotation> elements
* into the <xsd:choice> element directly, instead of writing
* <xsd:annotation> into the child elements of the <xsd:choice> */
if(wrapped.getClass().getName().endsWith("Group")) {
try {
Field fieldKind = clazz.getDeclaredField("kind");
fieldKind.setAccessible(true);
Object kind = fieldKind.get(wrapped);
if(kind == GroupKind.CHOICE) {
/*
* Note: For <annotation>, we only consider CHOICE groups, because
* for SEQUENCE groups we might run into the situation that
* multiple <annotation> elements are generated (for multiple
* child elements in the <sequence>), which is invalid.
*/
/* code below is taken from Tree$Group class! */
Particle c = (Particle)kind.getClass().getDeclaredMethod(
"write", ContentModelContainer.class).invoke(kind, parent);
wrapped.writeOccurs(c,isOptional,repeated);
Field fieldChildren = clazz.getDeclaredField("children");
fieldChildren.setAccessible(true);
Tree[] children = (Tree[])fieldChildren.get(wrapped);
XmlSchemaEnhancer.addXsdAnnotationsOutsideElement(elementInfo, c);
for (Tree child : children) {
child.write(c,false,false);
}
/* write all extension elements that need to go at the END
* of all children within the wrapping element. In particular,
* this affects <xs:assert> which can only appear at the end! */
XmlSchemaEnhancer.addXsdExtensionsAtEnd(elementInfo, c);
} else {
wrapped.write(parent, isOptional, repeated);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
wrapped.write(parent, isOptional, repeated);
}
}
@Override
Tree makeOptional(boolean really) {
return really? TreeWrapper.wrap(new Optional(this), elementInfo) :this;
}
@Override
Tree makeRepeated(boolean really) {
return really? TreeWrapper.wrap(new Repeated(this), elementInfo) :this;
}
@Override
boolean canBeTopLevel() {
return wrapped.canBeTopLevel();
}
@Override
boolean isNullable() {
return wrapped.isNullable();
}
/**
* "T?"
*/
private static final class Optional extends Tree {
private final Tree body;
private Optional(Tree body) {
this.body = body;
}
@Override
boolean isNullable() {
return true;
}
@Override
Tree makeOptional(boolean really) {
return this;
}
@Override
protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
body.write(parent,true,repeated);
}
}
/**
* "T+"
*/
private static final class Repeated extends Tree {
private final Tree body;
private Repeated(Tree body) {
this.body = body;
}
@Override
boolean isNullable() {
return body.isNullable();
}
@Override
Tree makeRepeated(boolean really) {
return this;
}
@Override
protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
body.write(parent,isOptional,true);
}
}
@Override
public String toString() {
return wrapped.toString();
}
@Override
public int hashCode() {
return wrapped.hashCode();
}
@Override
public boolean equals(Object obj) {
return wrapped.equals(obj);
}
}