in src/main/java/org/apache/xmlbeans/impl/schema/StscComplexTypeResolver.java [1544:1720]
static void buildStateMachine(SchemaParticle contentModel) {
if (contentModel == null) {
return;
}
SchemaParticleImpl partImpl = (SchemaParticleImpl) contentModel;
if (partImpl.hasTransitionNotes()) {
return;
}
QNameSetBuilder start = new QNameSetBuilder();
QNameSetBuilder excludenext = new QNameSetBuilder();
boolean deterministic = true;
SchemaParticle[] children;
boolean canskip = (partImpl.getMinOccurs().signum() == 0);
switch (partImpl.getParticleType()) {
case SchemaParticle.ELEMENT:
// compute start and excludeNext; canskip is already correct
if (partImpl.hasTransitionRules()) {
start.addAll(partImpl.acceptedStartNames());
} else {
start.add(partImpl.getName());
}
break;
case SchemaParticle.WILDCARD:
// compute start and excludeNext; canskip is already correct
start.addAll(partImpl.getWildcardSet());
break;
case SchemaParticle.SEQUENCE:
children = ensureStateMachine(partImpl.getParticleChildren());
// adjust canskip if all children are skippable
canskip = true;
for (int i = 0; canskip && i < children.length; i++) {
if (!(children[i]).isSkippable()) {
canskip = false;
}
}
// bubble up nondeterministic bit
for (SchemaParticle child : children) {
if (!((SchemaParticleImpl) child).isDeterministic()) {
deterministic = false;
break;
}
}
// verify deterministic and compute excludeNext set
for (int i = 1; i < children.length; i++) {
excludenext.addAll(((SchemaParticleImpl) children[i - 1]).getExcludeNextSet());
if (deterministic && !excludenext.isDisjoint((children[i]).acceptedStartNames())) {
deterministic = false;
}
if ((children[i]).isSkippable()) {
excludenext.addAll((children[i]).acceptedStartNames());
} else {
excludenext.clear();
}
}
// next, compute start set
for (SchemaParticle child : children) {
start.addAll(child.acceptedStartNames());
if (!child.isSkippable()) {
break;
}
}
break;
case SchemaParticle.CHOICE:
children = ensureStateMachine(partImpl.getParticleChildren());
// adjust canskip if any children are skippable
canskip = false;
for (SchemaParticle schemaParticle : children) {
if (schemaParticle.isSkippable()) {
canskip = true;
break;
}
}
// bubble up nondeterministic bit
for (SchemaParticle child : children) {
if (!((SchemaParticleImpl) child).isDeterministic()) {
deterministic = false;
break;
}
}
// compute start and excludeNext sets, verify deterministic
for (SchemaParticle child : children) {
if (deterministic && !start.isDisjoint(child.acceptedStartNames())) {
deterministic = false;
}
start.addAll(child.acceptedStartNames());
excludenext.addAll(((SchemaParticleImpl) child).getExcludeNextSet());
}
break;
case SchemaParticle.ALL:
children = ensureStateMachine(partImpl.getParticleChildren());
// adjust canskip if all children are skippable
canskip = true;
for (SchemaParticle child : children) {
if (!child.isSkippable()) {
canskip = false;
break;
}
}
// bubble up nondeterministic bit
for (SchemaParticle child : children) {
if (!((SchemaParticleImpl) child).isDeterministic()) {
deterministic = false;
break;
}
}
// compute start and excludeNext sets, verify deterministic
for (SchemaParticle child : children) {
if (deterministic && !start.isDisjoint(child.acceptedStartNames())) {
deterministic = false;
}
start.addAll(child.acceptedStartNames());
excludenext.addAll(((SchemaParticleImpl) child).getExcludeNextSet());
}
if (canskip) {
excludenext.addAll(start);
}
break;
default:
throw new IllegalStateException("Unrecognized schema particle");
}
// apply looping logic
BigInteger minOccurs = partImpl.getMinOccurs();
BigInteger maxOccurs = partImpl.getMaxOccurs();
boolean canloop = (maxOccurs == null || maxOccurs.compareTo(BigInteger.ONE) > 0);
boolean varloop = (maxOccurs == null || minOccurs.compareTo(maxOccurs) < 0);
if (canloop && deterministic && !excludenext.isDisjoint(start)) {
// we have a possible looping nondeterminism.
// let's take some time now to see if it's actually caused
// by non-unique-particle-attribute or not.
QNameSet suspectSet = excludenext.intersect(start);
// compute the set of all particles that could start this group
Map<SchemaParticle, QNameSet> startMap = new HashMap<>();
particlesMatchingStart(partImpl, suspectSet, startMap, new QNameSetBuilder());
// compute the set of all particles that could have been repeated rather than ending this group
Map<SchemaParticle, QNameSet> afterMap = new HashMap<>();
particlesMatchingAfter(partImpl, suspectSet, afterMap, new QNameSetBuilder(), true);
// see if we can find a member of after that is not a member of start
// if we can, then particle attribution is not unique
deterministic = afterMapSubsumedByStartMap(startMap, afterMap);
}
if (varloop) {
excludenext.addAll(start);
}
canskip = canskip || minOccurs.signum() == 0;
partImpl.setTransitionRules(start.toQNameSet(), canskip);
partImpl.setTransitionNotes(excludenext.toQNameSet(), deterministic);
}