package com.ctc.wstx.dtd;
import java.util.*;
import com.ctc.wstx.util.PrefixedName;
Content specification that defines model that has sequence of one or more
elements that have to come in the specified order.
/**
* Content specification that defines model that has sequence of one or more
* elements that have to come in the specified order.
*/
public class SeqContentSpec
extends ContentSpec
{
final boolean mNsAware;
final ContentSpec[] mContentSpecs;
/*
///////////////////////////////////////////////////
// Life-cycle
///////////////////////////////////////////////////
*/
public SeqContentSpec(boolean nsAware, char arity, ContentSpec[] subSpecs)
{
super(arity);
mNsAware = nsAware;
mContentSpecs = subSpecs;
}
public static SeqContentSpec construct(boolean nsAware, char arity, Collection<ContentSpec> subSpecs)
{
ContentSpec[] specs = new ContentSpec[subSpecs.size()];
subSpecs.toArray(specs);
return new SeqContentSpec(nsAware, arity, specs);
}
/*
///////////////////////////////////////////////////
// Public API
///////////////////////////////////////////////////
*/
@Override
public StructValidator getSimpleValidator()
{
/* Can we create a simple validator? Yes, if the sub-specs are
* all simple (leaves == element tokens with no arity modifier)
*/
ContentSpec[] specs = mContentSpecs;
int i = 0;
int len = specs.length;
for (; i < len; ++i) {
if (!specs[i].isLeaf()) {
break;
}
}
if (i == len) { // all leaves, kewl
PrefixedName[] set = new PrefixedName[len];
for (i = 0; i < len; ++i) {
TokenContentSpec ss = (TokenContentSpec) specs[i];
set[i] = ss.getName();
}
return new Validator(mArity, set);
}
// Nope, need a DFA:
return null;
}
@Override
public ModelNode rewrite()
{
/* First, need to create a tree of sub-models, consisting of
* binary concat nodes (as opposed to n-ary list). Can do that
* recursively (note that we'll always have at least 2 child
* nodes!)
*/
ModelNode model = rewrite(mContentSpecs, 0, mContentSpecs.length);
// and then resolve arity modifiers, if necessary:
if (mArity == '*') {
return new StarModel(model);
}
if (mArity == '?') {
return new OptionalModel(model);
}
if (mArity == '+') {
return new ConcatModel(model,
new StarModel(model.cloneModel()));
}
return model;
}
private ModelNode rewrite(ContentSpec[] specs, int first, int last)
{
// 3 or less, can convert and create; 4 or more, need to recurse:
int count = last - first;
if (count > 3) {
int mid = (last + first + 1) >> 1;
return new ConcatModel(rewrite(specs, first, mid),
rewrite(specs, mid, last));
}
ConcatModel model = new ConcatModel(specs[first].rewrite(),
specs[first+1].rewrite());
if (count == 3) {
model = new ConcatModel(model, specs[first+2].rewrite());
}
return model;
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append('(');
for (int i = 0; i < mContentSpecs.length; ++i) {
if (i > 0) {
sb.append(", ");
}
sb.append(mContentSpecs[i].toString());
}
sb.append(')');
if (mArity != ' ') {
sb.append(mArity);
}
return sb.toString();
}
/*
///////////////////////////////////////////////////
// Validator class that can be used for simple
// choices (including mixed content)
///////////////////////////////////////////////////
*/
Simple validator that can be used if all components of a sequence
are leaf nodes, ie. elements with no explicit arity modifiers.
/**
* Simple validator that can be used if all components of a sequence
* are leaf nodes, ie. elements with no explicit arity modifiers.
*/
final static class Validator
extends StructValidator
{
final char mArity;
final PrefixedName[] mNames;
Number of full repetitions done over the sequence
/**
* Number of full repetitions done over the sequence
*/
int mRounds = 0;
Expected next element in the sequence
/**
* Expected next element in the sequence
*/
int mStep = 0;
public Validator(char arity, PrefixedName[] names)
{
mArity = arity;
mNames = names;
}
Sequence content specification is always stateful; can not
use a shared instance... so let's create new instance:
/**
* Sequence content specification is always stateful; can not
* use a shared instance... so let's create new instance:
*/
@Override
public StructValidator newInstance() {
return new Validator(mArity, mNames);
}
@Override
public String tryToValidate(PrefixedName elemName)
{
// First; have we already done that max. 1 sequence?
if (mStep == 0 && mRounds == 1) {
if (mArity == '?' || mArity == ' ') {
return "was not expecting any more elements in the sequence ("
+concatNames(mNames)+")";
}
}
PrefixedName next = mNames[mStep];
if (!elemName.equals(next)) {
return expElem(mStep);
}
if (++mStep == mNames.length) {
++mRounds;
mStep = 0;
}
return null;
}
@Override
public String fullyValid()
{
if (mStep != 0) {
return expElem(mStep)+"; got end element";
}
switch (mArity) {
case '*':
case '?':
return null;
case '+': // need at least one (and multiples checked earlier)
case ' ':
if (mRounds > 0) {
return null;
}
return "Expected sequence ("+concatNames(mNames)+"); got end element";
}
// should never happen:
throw new IllegalStateException("Internal error");
}
private String expElem(int step)
{
return "expected element <"+mNames[step]+"> in sequence ("
+concatNames(mNames)+")";
}
final static String concatNames(PrefixedName[] names)
{
StringBuilder sb = new StringBuilder();
for (int i = 0, len = names.length; i < len; ++i) {
if (i > 0) {
sb.append(", ");
}
sb.append(names[i].toString());
}
return sb.toString();
}
}
}