// normalize each child component in turn
int count = topcomp.getChildCount();
boolean modified = false;
boolean compact = false;
for (int i = 0; i < count; i++) {
SchemaBase child = topcomp.getChild(i);
if (child instanceof OpenAttrBase) {
// start by normalizing the child component content
OpenAttrBase childcomp = (OpenAttrBase)child;
ComponentExtension childext = (ComponentExtension)childcomp.getExtension();
if (childext.normalize(depth+1)) {
modified = true;
}
// check for child components with special normalization handling
switch (childcomp.type()) {
case SchemaBase.ALL_TYPE:
case SchemaBase.CHOICE_TYPE:
case SchemaBase.SEQUENCE_TYPE:
{
// child component is a compositor, so see if it can be deleted or replaced
CommonCompositorDefinition compositor = (CommonCompositorDefinition)childcomp;
int size = compositor.getParticleList().size();
if (size == 0) {
// empty compositor, just remove (always safe to do this)
childext.setRemoved(true);
modified = true;
if (s_logger.isDebugEnabled()) {
s_logger.debug(lead + "eliminated empty compositor " + path);
}
} else if (size == 1) {
// single component in compositor, first try to convert compositor to singleton
OpenAttrBase grandchild = (OpenAttrBase)compositor.getParticleList().get(0);
IArity particle = (IArity)grandchild;
if (SchemaUtils.isRepeated(compositor)) {
if (!SchemaUtils.isRepeated(particle)) {
// repeated compositor with non-repeated particle, so pass repeat to particle
if (s_logger.isDebugEnabled()) {
s_logger.debug(lead + "passing repeat from compositor " +
SchemaUtils.componentPath(childcomp) + " to only child " +
SchemaUtils.describeComponent(grandchild));
}
particle.setMaxOccurs(compositor.getMaxOccurs());
((ComponentExtension)grandchild.getExtension()).setRepeated(true);
compositor.setMaxOccurs(null);
((ComponentExtension)compositor.getExtension()).setRepeated(false);
} else if ((compositor.getMaxOccurs().isUnbounded() &&
particle.getMaxOccurs().isUnbounded())) {
// unbounded compositor with unbounded particle, just wipe repeat from compositor
if (s_logger.isDebugEnabled()) {
s_logger.debug(lead + "clearing unbounded from compositor " +
SchemaUtils.componentPath(childcomp) + " with unbounded only child " +
SchemaUtils.describeComponent(grandchild));
}
compositor.setMaxOccurs(null);
((ComponentExtension)compositor.getExtension()).setRepeated(false);
}
}
if (SchemaUtils.isOptional(compositor)) {
if (SchemaUtils.isOptional(particle)) {
// optional compositor with optional particle, just wipe optional from compositor
if (s_logger.isDebugEnabled()) {
s_logger.debug(lead + "clearing optional from compositor " +
SchemaUtils.componentPath(childcomp) + " with optional only child " +
SchemaUtils.describeComponent(grandchild));
}
compositor.setMinOccurs(null);
((ComponentExtension)compositor.getExtension()).setOptional(false);
} else if (Count.isCountEqual(1, particle.getMinOccurs())) {
// optional compositor with required particle, so pass optional to particle
if (s_logger.isDebugEnabled()) {
s_logger.debug(lead + "passing optional from compositor " +
SchemaUtils.componentPath(childcomp) + " to required only child " +
SchemaUtils.describeComponent(grandchild));
}
particle.setMinOccurs(Count.COUNT_ZERO);
((ComponentExtension)grandchild.getExtension()).setOptional(true);
compositor.setMinOccurs(null);
((ComponentExtension)compositor.getExtension()).setOptional(false);
}
}
// check if top component is also a compositor
if (topcomp instanceof CommonCompositorDefinition) {
// nested compositor, check if can be simplified
if (SchemaUtils.isSingleton(compositor)) {
// nested singleton compositor with only child, just replace with the child
topcomp.replaceChild(i, grandchild);
modified = true;
if (s_logger.isDebugEnabled()) {
s_logger.debug(lead + "replacing singleton compositor " +
SchemaUtils.componentPath(childcomp) + " with only child " +
SchemaUtils.describeComponent(grandchild));
}
} else if (compositor instanceof ChoiceElement) {
// replace choice with sequence to simplify handling, since just one particle
if (s_logger.isDebugEnabled()) {
s_logger.debug(lead + "substituting sequence for choice " +
SchemaUtils.componentPath(childcomp) + " with only one child");
}
SequenceElement sequence = new SequenceElement();
sequence.setAnnotation(compositor.getAnnotation());
sequence.setExtension(compositor.getExtension());
sequence.setMaxOccurs(compositor.getMaxOccurs());
sequence.setMinOccurs(compositor.getMinOccurs());
sequence.getParticleList().add(grandchild);
topcomp.replaceChild(i, sequence);
modified = true;
}
}
}
break;
}
case SchemaBase.EXTENSION_TYPE:
{
if (childcomp instanceof SimpleExtensionElement) {
// replace empty simple type extension with base type
SimpleExtensionElement extend = (SimpleExtensionElement)childcomp;
if (extend.getAttributeList().size() == 0 && extend.getAnyAttribute() == null) {
modified = substituteTypeDerivation(lead, topcomp, childcomp, extend);
}
} else {
// replace empty complex type extension with base type
ComplexExtensionElement extend = (ComplexExtensionElement)child;
if (extend.getContentDefinition() == null && extend.getAttributeList().size() == 0 &&
extend.getAnyAttribute() == null) {
modified = substituteTypeDerivation(lead, topcomp, childcomp, extend);
}
}
break;
}
case SchemaBase.RESTRICTION_TYPE:
{
if (childcomp instanceof SimpleRestrictionElement) {
// replace simple type restriction with base type unless it has facets
SimpleRestrictionElement restrict = (SimpleRestrictionElement)childcomp;
if (restrict.getFacetsList().size() == 0 && restrict.getBase() != null) {
QName base = restrict.getBase();
if (base == null) {
// derivation using inline base type, just eliminate the restriction
} else {
modified = substituteTypeDerivation(lead, topcomp, childcomp, restrict);
}
}
} else {
// always replace complex type restriction with base type
ComplexRestrictionElement restrict = (ComplexRestrictionElement)child;
modified = substituteTypeDerivation(lead, topcomp, childcomp, restrict);
}
break;
}
}
// delete child component if flagged for removal
if (childext.isRemoved()) {
removeChild(i);
compact = true;
}
}
}
if (compact) {
topcomp.compactChildren();
modified = true;
}
// handle union normalization after all children have been normalized
if (topcomp.type() == SchemaBase.UNION_TYPE) {
// start by checking duplicates in the member types
compact = false;
UnionElement union = (UnionElement)topcomp;
Set typeset = new HashSet();
ArrayList keeptypes = new ArrayList();
QName[] membertypes = union.getMemberTypes();
if (membertypes != null) {
for (int i = 0; i < membertypes.length; i++) {
QName type = membertypes[i];
if (typeset.contains(type)) {
if (s_logger.isDebugEnabled()) {
s_logger.debug(lead + "removed redundant member type " + type + " from " + path);
}
} else {
typeset.add(type);
keeptypes.add(type);
}
}
}
// then check inline types for duplicates, simplifying where possible
count = union.getChildCount();
for (int i = 0; i < count; i++) {
SchemaBase child = topcomp.getChild(i);
if (child instanceof OpenAttrBase) {
// child schema component must be an inline simple type definition
SimpleTypeElement simple = (SimpleTypeElement)child;
boolean keeper = false;
SchemaBase derivation = simple.getDerivation();
QName repltype = null;
if (derivation != null) {
// keep the inline definition by default, but check for replacement cases
keeper = true;
if (derivation.type() == SchemaBase.RESTRICTION_TYPE) {
SimpleRestrictionElement innerrestrict = (SimpleRestrictionElement)derivation;
if (innerrestrict.getChildCount() == 0) {
// treat empty restriction the same as a member type from list
repltype = innerrestrict.getBase();
if (typeset.contains(repltype)) {
keeper = false;
}
}
} else if (derivation.type() == SchemaBase.UNION_TYPE) {
UnionElement innerunion = (UnionElement)derivation;
QName[] innertypes = innerunion.getMemberTypes();
FilteredSegmentList innerinlines = innerunion.getInlineBaseList();
if (innertypes.length + innerinlines.size() == 1) {