com.thaiopensource.relaxng.pattern.DataDerivFunction Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of wicketstuff-jing Show documentation
Show all versions of wicketstuff-jing Show documentation
Jing is a validator for RELAX NG and other schema languages. This
project was taken from http://code.google.com/p/jing-trang and
mavenized for inclusion in the Wicket Stuff HTML Validator.
The code was taken from the 20091111 release.
package com.thaiopensource.relaxng.pattern;
import org.relaxng.datatype.Datatype;
import org.relaxng.datatype.ValidationContext;
import org.relaxng.datatype.DatatypeException;
import java.util.List;
// invariant: if return is not notAllowed, then no failures are added to fail
class DataDerivFunction extends AbstractPatternFunction {
private final ValidatorPatternBuilder builder;
private final ValidationContext vc;
private final String str;
private final List fail;
DataDerivFunction(String str, ValidationContext vc, ValidatorPatternBuilder builder, List fail) {
this.str = str;
this.vc = vc;
this.builder = builder;
this.fail = fail;
}
static boolean isBlank(String str) {
int len = str.length();
for (int i = 0; i < len; i++) {
switch (str.charAt(i)) {
case '\r':
case '\n':
case ' ':
case '\t':
break;
default:
return false;
}
}
return true;
}
public Pattern caseText(TextPattern p) {
return p;
}
public Pattern caseRef(RefPattern p) {
return memoApply(p.getPattern());
}
public Pattern caseList(ListPattern p) {
int len = str.length();
int tokenIndex = 0;
int tokenStart = -1;
PatternMemo memo = builder.getPatternMemo(p.getOperand());
for (int i = 0; i < len; i++) {
switch (str.charAt(i)) {
case '\r':
case '\n':
case ' ':
case '\t':
if (tokenStart >= 0) {
memo = tokenDeriv(memo, tokenIndex++, tokenStart, i);
tokenStart = -1;
}
break;
default:
if (tokenStart < 0)
tokenStart = i;
break;
}
}
if (tokenStart >= 0)
memo = tokenDeriv(memo, tokenIndex++, tokenStart, len);
if (memo.getPattern().isNullable())
return builder.makeEmpty();
if (memo.isNotAllowed())
return memo.getPattern();
// pseudo-token to try and force some failures
tokenDeriv(memo, tokenIndex, len, len);
// XXX handle the case where this didn't produce any failures
return builder.makeNotAllowed();
}
private PatternMemo tokenDeriv(PatternMemo p, int tokenIndex, int start, int end) {
int failStartSize = failSize();
PatternMemo deriv = p.dataDeriv(str.substring(start, end), vc, fail);
if (fail != null && deriv.isNotAllowed()) {
for (int i = fail.size() - 1; i >= failStartSize; --i)
fail.get(i).setToken(tokenIndex, start, end);
}
return deriv;
}
public Pattern caseValue(ValuePattern p) {
Datatype dt = p.getDatatype();
Object value = dt.createValue(str, vc);
if (value != null && dt.sameValue(p.getValue(), value))
return builder.makeEmpty();
if (fail != null) {
if (value == null) {
try {
dt.checkValid(str, vc);
}
catch (DatatypeException e) {
fail.add(new DataDerivFailure(dt, p.getDatatypeName(), e));
}
}
else
fail.add(new DataDerivFailure(p));
}
return builder.makeNotAllowed();
}
public Pattern caseData(DataPattern p) {
if (p.allowsAnyString())
return builder.makeEmpty();
if (fail != null) {
try {
p.getDatatype().checkValid(str, vc);
return builder.makeEmpty();
}
catch (DatatypeException e) {
fail.add(new DataDerivFailure(p, e));
return builder.makeNotAllowed();
}
}
if (p.getDatatype().isValid(str, vc))
return builder.makeEmpty();
else
return builder.makeNotAllowed();
}
public Pattern caseDataExcept(DataExceptPattern p) {
Pattern tem = caseData(p);
if (tem.isNullable() && memoApply(p.getExcept()).isNullable()) {
if (fail != null)
fail.add(new DataDerivFailure(p));
return builder.makeNotAllowed();
}
return tem;
}
public Pattern caseAfter(AfterPattern p) {
Pattern p1 = p.getOperand1();
final int failStartSize = failSize();
if (memoApplyWithFailure(p1).isNullable())
return p.getOperand2();
if (p1.isNullable() && isBlank(str)) {
clearFailures(failStartSize);
return p.getOperand2();
}
return builder.makeNotAllowed();
}
public Pattern caseChoice(ChoicePattern p) {
final int failStartSize = failSize();
Pattern tem = builder.makeChoice(memoApplyWithFailure(p.getOperand1()),
memoApplyWithFailure(p.getOperand2()));
if (!tem.isNotAllowed())
clearFailures(failStartSize);
return tem;
}
public Pattern caseGroup(GroupPattern p) {
final int failStartSize = failSize();
final Pattern p1 = p.getOperand1();
final Pattern p2 = p.getOperand2();
Pattern tem = builder.makeGroup(memoApplyWithFailure(p1), p2);
if (p1.isNullable())
tem = builder.makeChoice(tem, memoApplyWithFailure(p2));
if (!tem.isNotAllowed())
clearFailures(failStartSize);
return tem;
}
// list//interleave is prohibited, so I don't think this can happen
public Pattern caseInterleave(InterleavePattern p) {
final Pattern p1 = p.getOperand1();
final Pattern p2 = p.getOperand2();
return builder.makeChoice(builder.makeInterleave(memoApply(p1), p2),
builder.makeInterleave(p1, memoApply(p2)));
}
public Pattern caseOneOrMore(OneOrMorePattern p) {
return builder.makeGroup(memoApplyWithFailure(p.getOperand()),
builder.makeOptional(p));
}
public Pattern caseOther(Pattern p) {
return builder.makeNotAllowed();
}
private Pattern memoApply(Pattern p) {
return builder.getPatternMemo(p).dataDeriv(str, vc).getPattern();
}
private Pattern memoApplyWithFailure(Pattern p) {
return builder.getPatternMemo(p).dataDeriv(str, vc, fail).getPattern();
}
private int failSize() {
return fail == null ? 0 : fail.size();
}
private void clearFailures(int failStartSize) {
if (fail != null) {
for (int i = fail.size() - 1; i >= failStartSize; --i)
fail.remove(i);
}
}
}