blob: 75279b609abcbbd4fa471f4dabbd811f40912066 [file] [log] [blame]
/* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.xmlbeans.impl.inst2xsd;
import org.apache.xmlbeans.*;
import org.apache.xmlbeans.impl.common.ValidationContext;
import org.apache.xmlbeans.impl.common.XmlWhitespace;
import org.apache.xmlbeans.impl.inst2xsd.util.Attribute;
import org.apache.xmlbeans.impl.inst2xsd.util.Element;
import org.apache.xmlbeans.impl.inst2xsd.util.Type;
import org.apache.xmlbeans.impl.inst2xsd.util.TypeSystemHolder;
import org.apache.xmlbeans.impl.util.XsTypeConverter;
import org.apache.xmlbeans.impl.values.*;
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Cezar Andrei ( cezar.andrei at bea.com )
* Date: Jul 26, 2004
*/
public class RussianDollStrategy
implements XsdGenStrategy {
static final String _xsi = "http://www.w3.org/2001/XMLSchema-instance";
static final QName _xsiNil = new QName(_xsi, "nil", "xsi");
public void processDoc(XmlObject[] instances, Inst2XsdOptions options, TypeSystemHolder typeSystemHolder) {
for (XmlObject instance : instances) {
XmlCursor xc = instance.newCursor();
// xc on start doc
StringBuilder comment = new StringBuilder();
while (!xc.isStart()) {
xc.toNextToken();
if (xc.isComment()) {
comment.append(xc.getTextValue());
} else if (xc.isEnddoc()) {
return;
}
}
// xc now on the root element
Element withElem = processElement(xc, comment.toString(), options, typeSystemHolder);
withElem.setGlobal(true);
addGlobalElement(withElem, typeSystemHolder, options);
}
}
protected Element addGlobalElement(Element withElem, TypeSystemHolder typeSystemHolder, Inst2XsdOptions options) {
assert withElem.isGlobal();
Element intoElem = typeSystemHolder.getGlobalElement(withElem.getName());
if (intoElem == null) {
typeSystemHolder.addGlobalElement(withElem);
return withElem;
} else {
combineTypes(intoElem.getType(), withElem.getType(), options);
combineElementComments(intoElem, withElem);
return intoElem;
}
}
protected Element processElement(XmlCursor xc, String comment,
Inst2XsdOptions options, TypeSystemHolder typeSystemHolder) {
assert xc.isStart();
Element element = new Element();
element.setName(xc.getName());
element.setGlobal(false);
Type elemType = Type.createUnnamedType(Type.SIMPLE_TYPE_SIMPLE_CONTENT); //assume simple, set later
element.setType(elemType);
StringBuilder textBuff = new StringBuilder();
StringBuilder commentBuff = new StringBuilder();
List<Element> children = new ArrayList<>();
List<Attribute> attributes = new ArrayList<>();
loop:
do {
XmlCursor.TokenType tt = xc.toNextToken();
switch (tt.intValue()) {
case XmlCursor.TokenType.INT_ATTR:
// todo check for xsi:type
// ignore xsi:... attributes other than xsi:nil
QName attName = xc.getName();
if (!_xsiNil.getNamespaceURI().equals(attName.getNamespaceURI())) {
attributes.add(processAttribute(xc, options, element.getName().getNamespaceURI(), typeSystemHolder));
} else if (_xsiNil.equals(attName)) {
element.setNillable(true);
}
break;
case XmlCursor.TokenType.INT_START:
children.add(processElement(xc, commentBuff.toString(), options, typeSystemHolder));
commentBuff.delete(0, commentBuff.length());
break;
case XmlCursor.TokenType.INT_TEXT:
textBuff.append(xc.getChars());
break;
case XmlCursor.TokenType.INT_COMMENT:
commentBuff.append(xc.getTextValue());
break;
case XmlCursor.TokenType.INT_NAMESPACE:
// ignore,
// each element and attribute will take care to define itself in the right targetNamespace
break;
case XmlCursor.TokenType.INT_END:
break loop;
case XmlCursor.TokenType.INT_PROCINST:
// ignore
break;
case XmlCursor.TokenType.INT_ENDDOC:
break loop;
case XmlCursor.TokenType.INT_NONE:
break loop;
case XmlCursor.TokenType.INT_STARTDOC:
throw new IllegalStateException();
default:
throw new IllegalStateException("Unknown TokenType.");
}
}
while (true);
String collapsedText = XmlWhitespace.collapse(textBuff.toString(), XmlWhitespace.WS_COLLAPSE);
String commnetStr = (comment == null ?
(commentBuff.length() == 0 ? null : commentBuff.toString()) :
(commentBuff.length() == 0 ? comment : commentBuff.insert(0, comment).toString()));
element.setComment(commnetStr);
if (children.size() > 0) {
// complex content
if (collapsedText.length() > 0) {
elemType.setContentType(Type.COMPLEX_TYPE_MIXED_CONTENT);
} else {
elemType.setContentType(Type.COMPLEX_TYPE_COMPLEX_CONTENT);
}
processElementsInComplexType(elemType, children, element.getName().getNamespaceURI(), typeSystemHolder, options);
processAttributesInComplexType(elemType, attributes);
} else {
// simple content
// hack workaround for being able to call xc.getNamespaceForPrefix()
XmlCursor xcForNamespaces = xc.newCursor();
xcForNamespaces.toParent();
if (attributes.size() > 0) {
elemType.setContentType(Type.COMPLEX_TYPE_SIMPLE_CONTENT);
Type extendedType = Type.createNamedType(
processSimpleContentType(textBuff.toString(), options, xcForNamespaces), Type.SIMPLE_TYPE_SIMPLE_CONTENT);
elemType.setExtensionType(extendedType);
processAttributesInComplexType(elemType, attributes);
} else {
elemType.setContentType(Type.SIMPLE_TYPE_SIMPLE_CONTENT);
elemType.setName(processSimpleContentType(textBuff.toString(), options, xcForNamespaces));
// add enumeration value
String enumValue = XmlString.type.getName().equals(elemType.getName()) ? textBuff.toString() : collapsedText;
elemType.addEnumerationValue(enumValue, xcForNamespaces);
}
xcForNamespaces.dispose(); // end hack
}
checkIfReferenceToGlobalTypeIsNeeded(element, typeSystemHolder, options);
return element;
}
protected void processElementsInComplexType(Type elemType, List<Element> children, String parentNamespace,
TypeSystemHolder typeSystemHolder, Inst2XsdOptions options) {
Map<QName, Element> elemNamesToElements = new HashMap<>();
Element currentElem = null;
for (Element child : children) {
if (currentElem == null) { // first element in this type
checkIfElementReferenceIsNeeded(child, parentNamespace, typeSystemHolder, options);
elemType.addElement(child);
elemNamesToElements.put(child.getName(), child);
currentElem = child;
continue;
}
if (currentElem.getName() == child.getName()) { // same contiguos element
combineTypes(currentElem.getType(), child.getType(), options); // unify types
combineElementComments(currentElem, child);
// minOcc=0 maxOcc=unbounded
currentElem.setMinOccurs(0);
currentElem.setMaxOccurs(Element.UNBOUNDED);
} else {
Element sameElem = elemNamesToElements.get(child.getName());
if (sameElem == null) { // new element name
checkIfElementReferenceIsNeeded(child, parentNamespace, typeSystemHolder, options);
elemType.addElement(child);
elemNamesToElements.put(child.getName(), child);
} else { //same non contiguos
combineTypes(currentElem.getType(), child.getType(), options);
combineElementComments(currentElem, child);
elemType.setTopParticleForComplexOrMixedContent(Type.PARTICLE_CHOICE_UNBOUNDED);
}
currentElem = child;
}
}
}
protected void checkIfElementReferenceIsNeeded(Element child, String parentNamespace,
TypeSystemHolder typeSystemHolder, Inst2XsdOptions options) {
if (!child.getName().getNamespaceURI().equals(parentNamespace)) {
Element referencedElem = new Element();
referencedElem.setGlobal(true);
referencedElem.setName(child.getName());
referencedElem.setType(child.getType());
if (child.isNillable()) {
referencedElem.setNillable(true);
child.setNillable(false);
}
referencedElem = addGlobalElement(referencedElem, typeSystemHolder, options);
child.setRef(referencedElem); // clears child's type
}
}
protected void checkIfReferenceToGlobalTypeIsNeeded(Element elem, TypeSystemHolder typeSystemHolder,
Inst2XsdOptions options) {
// RussianDollDesign doesn't define global types
}
protected void processAttributesInComplexType(Type elemType, List<Attribute> attributes) {
assert elemType.isComplexType();
for (Attribute att : attributes) {
elemType.addAttribute(att);
}
}
protected Attribute processAttribute(XmlCursor xc, Inst2XsdOptions options, String parentNamespace,
TypeSystemHolder typeSystemHolder) {
assert xc.isAttr() : "xc not on attribute";
Attribute attribute = new Attribute();
QName attName = xc.getName();
attribute.setName(attName);
XmlCursor parent = xc.newCursor();
parent.toParent();
Type simpleContentType = Type.createNamedType(
processSimpleContentType(xc.getTextValue(), options, parent), Type.SIMPLE_TYPE_SIMPLE_CONTENT);
parent.dispose();
attribute.setType(simpleContentType);
checkIfAttributeReferenceIsNeeded(attribute, parentNamespace, typeSystemHolder);
return attribute;
}
protected void checkIfAttributeReferenceIsNeeded(Attribute attribute, String parentNamespace, TypeSystemHolder typeSystemHolder) {
if (!attribute.getName().getNamespaceURI().equals("") &&
!attribute.getName().getNamespaceURI().equals(parentNamespace)) {
// make attribute be a reference to a top level attribute in a different targetNamespace
Attribute referencedAtt = new Attribute();
referencedAtt.setGlobal(true);
referencedAtt.setName(attribute.getName());
referencedAtt.setType(attribute.getType());
typeSystemHolder.addGlobalAttribute(referencedAtt);
attribute.setRef(referencedAtt);
}
}
protected static class SCTValidationContext
implements ValidationContext {
protected boolean valid = true;
public boolean isValid() {
return valid;
}
public void resetToValid() {
valid = true;
}
public void invalid(String message) {
valid = false;
}
public void invalid(String code, Object[] args) {
valid = false;
}
}
private final SCTValidationContext _validationContext = new SCTValidationContext();
// List of precedence for smart simple primitive type determination
// byte, short, int, long, integer, float, double, decimal,
// boolean
// date, dateTime, time, gDuration,
// QName ?,
// anyUri ? - triggered only for http:// or www. constructs,
// list types ?
// string
protected QName processSimpleContentType(String lexicalValue, Inst2XsdOptions options, final XmlCursor xc) {
// check options and return xsd:string or if smart is enabled, look for a better type
if (options.getSimpleContentTypes() == Inst2XsdOptions.SIMPLE_CONTENT_TYPES_STRING) {
return XmlString.type.getName();
}
if (options.getSimpleContentTypes() != Inst2XsdOptions.SIMPLE_CONTENT_TYPES_SMART) {
throw new IllegalArgumentException("Unknown value for Inst2XsdOptions.getSimpleContentTypes() :" + options.getSimpleContentTypes());
}
// Inst2XsdOptions.SIMPLE_CONTENT_TYPES_SMART case
try {
XsTypeConverter.lexByte(lexicalValue);
return XmlByte.type.getName();
} catch (Exception ignored) {
}
try {
XsTypeConverter.lexShort(lexicalValue);
return XmlShort.type.getName();
} catch (Exception ignored) {
}
try {
XsTypeConverter.lexInt(lexicalValue);
return XmlInt.type.getName();
} catch (Exception ignored) {
}
try {
XsTypeConverter.lexLong(lexicalValue);
return XmlLong.type.getName();
} catch (Exception ignored) {
}
try {
XsTypeConverter.lexInteger(lexicalValue);
return XmlInteger.type.getName();
} catch (Exception ignored) {
}
try {
XsTypeConverter.lexFloat(lexicalValue);
return XmlFloat.type.getName();
} catch (Exception ignored) {
}
// // this not needed because it's lexical space is covered by float
// try
// {
// XsTypeConverter.lexDouble(lexicalValue);
// return XmlDouble.type.getName();
// }
// catch (Exception e) {}
//
// try
// {
// XsTypeConverter.lexDecimal(lexicalValue);
// return XmlDecimal.type.getName();
// }
// catch (Exception e) {}
XmlDateImpl.validateLexical(lexicalValue, XmlDate.type, _validationContext);
if (_validationContext.isValid()) {
return XmlDate.type.getName();
}
_validationContext.resetToValid();
XmlDateTimeImpl.validateLexical(lexicalValue, XmlDateTime.type, _validationContext);
if (_validationContext.isValid()) {
return XmlDateTime.type.getName();
}
_validationContext.resetToValid();
XmlTimeImpl.validateLexical(lexicalValue, XmlTime.type, _validationContext);
if (_validationContext.isValid()) {
return XmlTime.type.getName();
}
_validationContext.resetToValid();
XmlDurationImpl.validateLexical(lexicalValue, XmlDuration.type, _validationContext);
if (_validationContext.isValid()) {
return XmlDuration.type.getName();
}
_validationContext.resetToValid();
// check for uri
if (lexicalValue.startsWith("http://") || lexicalValue.startsWith("www.")) {
XmlAnyUriImpl.validateLexical(lexicalValue, _validationContext);
if (_validationContext.isValid()) {
return XmlAnyURI.type.getName();
}
_validationContext.resetToValid();
}
// check for QName
int idx = lexicalValue.indexOf(':');
if (idx >= 0 && idx == lexicalValue.lastIndexOf(':') && idx + 1 < lexicalValue.length()) {
XmlQNameImpl.validateLexical(lexicalValue, _validationContext, xc::namespaceForPrefix);
if (_validationContext.isValid()) {
return XmlQName.type.getName();
}
_validationContext.resetToValid();
}
//the check for lists is probably too expensive
return XmlString.type.getName();
}
protected void combineTypes(Type into, Type with, Inst2XsdOptions options) {
if (into == with) {
return;
}
if (into.isGlobal() && with.isGlobal() && into.getName().equals(with.getName())) {
return;
}
if (into.getContentType() == Type.SIMPLE_TYPE_SIMPLE_CONTENT &&
with.getContentType() == Type.SIMPLE_TYPE_SIMPLE_CONTENT) {
combineSimpleTypes(into, with, options);
return;
}
if ((into.getContentType() == Type.SIMPLE_TYPE_SIMPLE_CONTENT ||
into.getContentType() == Type.COMPLEX_TYPE_SIMPLE_CONTENT) &&
(with.getContentType() == Type.SIMPLE_TYPE_SIMPLE_CONTENT ||
with.getContentType() == Type.COMPLEX_TYPE_SIMPLE_CONTENT)) {
// take the extension name if it's a complex type
QName intoTypeName = into.isComplexType() ? into.getExtensionType().getName() : into.getName();
QName withTypeName = with.isComplexType() ? with.getExtensionType().getName() : with.getName();
//complex type simple content
into.setContentType(Type.COMPLEX_TYPE_SIMPLE_CONTENT);
QName moreGeneralTypeName = combineToMoreGeneralSimpleType(intoTypeName, withTypeName);
if (into.isComplexType()) {
Type extendedType = Type.createNamedType(moreGeneralTypeName, Type.SIMPLE_TYPE_SIMPLE_CONTENT);
into.setExtensionType(extendedType);
} else {
into.setName(moreGeneralTypeName);
}
combineAttributesOfTypes(into, with);
return;
}
if (into.getContentType() == Type.COMPLEX_TYPE_COMPLEX_CONTENT &&
with.getContentType() == Type.COMPLEX_TYPE_COMPLEX_CONTENT) {
combineAttributesOfTypes(into, with);
combineElementsOfTypes(into, with, options);
return;
}
if (into.getContentType() == Type.SIMPLE_TYPE_SIMPLE_CONTENT ||
into.getContentType() == Type.COMPLEX_TYPE_SIMPLE_CONTENT ||
with.getContentType() == Type.SIMPLE_TYPE_SIMPLE_CONTENT ||
with.getContentType() == Type.COMPLEX_TYPE_SIMPLE_CONTENT) {
into.setContentType(Type.COMPLEX_TYPE_MIXED_CONTENT);
combineAttributesOfTypes(into, with);
combineElementsOfTypes(into, with, options);
return;
}
if ((into.getContentType() == Type.SIMPLE_TYPE_SIMPLE_CONTENT ||
into.getContentType() == Type.COMPLEX_TYPE_SIMPLE_CONTENT ||
into.getContentType() == Type.COMPLEX_TYPE_COMPLEX_CONTENT ||
into.getContentType() == Type.COMPLEX_TYPE_MIXED_CONTENT) &&
(with.getContentType() == Type.SIMPLE_TYPE_SIMPLE_CONTENT ||
with.getContentType() == Type.COMPLEX_TYPE_SIMPLE_CONTENT ||
with.getContentType() == Type.COMPLEX_TYPE_COMPLEX_CONTENT ||
with.getContentType() == Type.COMPLEX_TYPE_MIXED_CONTENT)) {
into.setContentType(Type.COMPLEX_TYPE_MIXED_CONTENT);
combineAttributesOfTypes(into, with);
combineElementsOfTypes(into, with, options);
return;
}
throw new IllegalArgumentException("Unknown content type.");
}
protected void combineSimpleTypes(Type into, Type with, Inst2XsdOptions options) {
assert (into.getContentType() == Type.SIMPLE_TYPE_SIMPLE_CONTENT &&
with.getContentType() == Type.SIMPLE_TYPE_SIMPLE_CONTENT) : "Invalid arguments";
//simple type simple content
into.setName(combineToMoreGeneralSimpleType(into.getName(), with.getName()));
// take care of enumeration values
if (options.isUseEnumerations()) {
into.addAllEnumerationsFrom(with);
if (into.getEnumerationValues().size() > options.getUseEnumerations()) {
into.closeEnumeration();
}
}
}
protected QName combineToMoreGeneralSimpleType(QName t1, QName t2) {
if (t1.equals(t2)) {
return t1;
}
if (t2.equals(XmlShort.type.getName()) && t1.equals(XmlByte.type.getName())) {
return t2;
}
if (t1.equals(XmlShort.type.getName()) && t2.equals(XmlByte.type.getName())) {
return t1;
}
if (t2.equals(XmlInt.type.getName()) &&
(t1.equals(XmlShort.type.getName()) || t1.equals(XmlByte.type.getName()))) {
return t2;
}
if (t1.equals(XmlInt.type.getName()) &&
(t2.equals(XmlShort.type.getName()) || t2.equals(XmlByte.type.getName()))) {
return t1;
}
if (t2.equals(XmlLong.type.getName()) &&
(t1.equals(XmlInt.type.getName()) || t1.equals(XmlShort.type.getName()) || t1.equals(XmlByte.type.getName()))) {
return t2;
}
if (t1.equals(XmlLong.type.getName()) &&
(t2.equals(XmlInt.type.getName()) || t2.equals(XmlShort.type.getName()) || t2.equals(XmlByte.type.getName()))) {
return t1;
}
if (t2.equals(XmlInteger.type.getName()) &&
(t1.equals(XmlLong.type.getName()) || t1.equals(XmlInt.type.getName()) ||
t1.equals(XmlShort.type.getName()) || t1.equals(XmlByte.type.getName()))) {
return t2;
}
if (t1.equals(XmlInteger.type.getName()) &&
(t2.equals(XmlLong.type.getName()) || t2.equals(XmlInt.type.getName()) ||
t2.equals(XmlShort.type.getName()) || t2.equals(XmlByte.type.getName()))) {
return t1;
}
if (t2.equals(XmlFloat.type.getName()) &&
(t1.equals(XmlInteger.type.getName()) ||
t1.equals(XmlLong.type.getName()) || t1.equals(XmlInt.type.getName()) ||
t1.equals(XmlShort.type.getName()) || t1.equals(XmlByte.type.getName()))) {
return t2;
}
if (t1.equals(XmlFloat.type.getName()) &&
(t2.equals(XmlInteger.type.getName()) ||
t2.equals(XmlLong.type.getName()) || t2.equals(XmlInt.type.getName()) ||
t2.equals(XmlShort.type.getName()) || t2.equals(XmlByte.type.getName()))) {
return t1;
}
//double, decimal will never get here since they don't get generated
//the rest of the combinations are not compatible, so they will combine in xsd:string
return XmlString.type.getName();
}
protected void combineAttributesOfTypes(Type into, Type from) {
// loop through attributes: add fromAtt if they don't exist, combine them if they exist
outterLoop:
for (int i = 0; i < from.getAttributes().size(); i++) {
Attribute fromAtt = from.getAttributes().get(i);
for (int j = 0; j < into.getAttributes().size(); j++) {
Attribute intoAtt = into.getAttributes().get(j);
if (intoAtt.getName().equals(fromAtt.getName())) {
intoAtt.getType().setName(
combineToMoreGeneralSimpleType(intoAtt.getType().getName(), fromAtt.getType().getName()));
continue outterLoop;
}
}
// fromAtt doesn't exist in into type, will add it right now
into.addAttribute(fromAtt);
}
//optional attributes: if there are atts in into that are not in from, make them optional
for (int i = 0; i < into.getAttributes().size(); i++) {
Attribute intoAtt = into.getAttributes().get(i);
for (int j = 0; j < from.getAttributes().size(); j++) {
Attribute fromAtt = from.getAttributes().get(j);
if (fromAtt.getName().equals(intoAtt.getName())) {
continue;
}
}
// intoAtt doesn't exist in into type, will add it right now
intoAtt.setOptional(true);
}
}
protected void combineElementsOfTypes(Type into, Type from, Inst2XsdOptions options) {
boolean needsUnboundedChoice = false;
if (into.getTopParticleForComplexOrMixedContent() != Type.PARTICLE_SEQUENCE ||
from.getTopParticleForComplexOrMixedContent() != Type.PARTICLE_SEQUENCE) {
needsUnboundedChoice = true;
}
List<Element> res = new ArrayList<>();
int fromStartingIndex = 0;
int fromMatchedIndex = -1;
int intoMatchedIndex = -1;
// for each element in into
for (int i = 0; !needsUnboundedChoice && i < into.getElements().size(); i++) {
// try to find one with same name in from
Element intoElement = into.getElements().get(i);
for (int j = fromStartingIndex; j < from.getElements().size(); j++) {
Element fromElement = from.getElements().get(j);
if (intoElement.getName().equals(fromElement.getName())) {
fromMatchedIndex = j;
break;
}
}
// if not found, it's safe to add this one to result 'res' (as optional) and continue
if (fromMatchedIndex < fromStartingIndex) {
res.add(intoElement);
intoElement.setMinOccurs(0);
continue;
}
// else try out all from elemens between fromStartingIndex to fromMatchedIndex
// to see if they match one of the into elements
intoMatchingLoop:
for (int j2 = fromStartingIndex; j2 < fromMatchedIndex; j2++) {
Element fromCandidate = from.getElements().get(j2);
for (int i2 = i + 1; i2 < into.getElements().size(); i2++) {
Element intoCandidate = into.getElements().get(i2);
if (fromCandidate.getName().equals(intoCandidate.getName())) {
intoMatchedIndex = i2;
break intoMatchingLoop;
}
}
}
if (intoMatchedIndex < i) {
// if none matched they are safe to be added to res as optional
for (int j3 = fromStartingIndex; j3 < fromMatchedIndex; j3++) {
Element fromCandidate = from.getElements().get(j3);
res.add(fromCandidate);
fromCandidate.setMinOccurs(0);
}
// also since into[i] == from[fromMatchedIndex] add it only once
res.add(intoElement);
Element fromMatchedElement = from.getElements().get(fromMatchedIndex);
if (fromMatchedElement.getMinOccurs() <= 0) {
intoElement.setMinOccurs(0);
}
if (fromMatchedElement.getMaxOccurs() == Element.UNBOUNDED) {
intoElement.setMaxOccurs(Element.UNBOUNDED);
}
combineTypes(intoElement.getType(), fromMatchedElement.getType(), options);
combineElementComments(intoElement, fromMatchedElement);
fromStartingIndex = fromMatchedIndex + 1;
} else {
// if matched it means into type will transform into a choice unbounded type
needsUnboundedChoice = true;
}
}
for (int j = fromStartingIndex; j < from.getElements().size(); j++) {
Element remainingFromElement = from.getElements().get(j);
res.add(remainingFromElement);
remainingFromElement.setMinOccurs(0);
}
// if choice was detected
if (needsUnboundedChoice) {
into.setTopParticleForComplexOrMixedContent(Type.PARTICLE_CHOICE_UNBOUNDED);
outterLoop:
for (int j = 0; j < from.getElements().size(); j++) {
Element fromElem = from.getElements().get(j);
for (int i = 0; i < into.getElements().size(); i++) {
Element intoElem = into.getElements().get(i);
intoElem.setMinOccurs(1);
intoElem.setMaxOccurs(1);
if (intoElem == fromElem) {
continue outterLoop;
}
if (intoElem.getName().equals(fromElem.getName())) {
combineTypes(intoElem.getType(), fromElem.getType(), options);
combineElementComments(intoElem, fromElem);
continue outterLoop;
}
}
// fromElem doesn't exist in into type, will add it right now
into.addElement(fromElem);
fromElem.setMinOccurs(1);
fromElem.setMaxOccurs(1);
}
} else {
// into remains sequence but will contain the new list of elements res
into.setElements(res);
}
}
protected void combineElementComments(Element into, Element with) {
if (with.getComment() != null && with.getComment().length() > 0) {
if (into.getComment() == null) {
into.setComment(with.getComment());
} else {
into.setComment(into.getComment() + with.getComment());
}
}
}
}