com.thaiopensource.relaxng.pattern.PatternDumper 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 com.thaiopensource.util.VoidValue;
import com.thaiopensource.xml.util.Name;
import com.thaiopensource.xml.util.WellKnownNamespaces;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class PatternDumper {
private static final String INTERNAL_NAMESPACE = "http://www.thaiopensource.com/relaxng/internal";
private boolean startTagOpen = false;
private final ArrayList tagStack = new ArrayList();
private final StringBuilder buf;
private int level = 0;
private boolean suppressIndent = false;
private final List patternList = new ArrayList();
private final Map localNamePatternCount = new HashMap();
private int otherPatternCount;
private final Map patternNameMap = new HashMap();
private final PatternFunction dumper = new Dumper();
private final PatternFunction elementDumper = new ElementDumper();
private final PatternFunction optionalDumper = new OptionalDumper();
private final PatternFunction groupDumper = new GroupDumper();
private final PatternFunction choiceDumper = new ChoiceDumper();
private final PatternFunction interleaveDumper = new InterleaveDumper();
private final NameClassVisitor nameClassDumper = new NameClassDumper();
private final NameClassVisitor choiceNameClassDumper = new ChoiceNameClassDumper();
static public String toString(Pattern p) {
return new PatternDumper().dump(p).getSchema();
}
private PatternDumper() {
buf = new StringBuilder();
}
private String getSchema() {
return buf.toString();
}
private PatternDumper dump(Pattern p) {
write("");
startElement("grammar");
attribute("xmlns", WellKnownNamespaces.RELAX_NG);
startElement("start");
p.apply(dumper);
endElement();
for (int i = 0; i < patternList.size(); i++) {
startElement("define");
ElementPattern tem = patternList.get(i);
attribute("name", getName(tem));
tem.apply(elementDumper);
endElement();
}
endElement();
write('\n');
return this;
}
private String getName(ElementPattern p) {
String name = patternNameMap.get(p);
// patterns for element patterns with local name X are named: X, X_2, X_3
// however if X is of the form Y_N (N > 0), then the patterns are named: X_1, X_2, X_3
// for element patterns with complex name classes, the patterns are named: _1, _2, _3
if (name == null) {
NameClass nc = p.getNameClass();
if (nc instanceof SimpleNameClass) {
String localName = ((SimpleNameClass)nc).getName().getLocalName();
Integer i = localNamePatternCount.get(localName);
if (i == null) {
i = 1;
name = localName;
// see if the name can be the same as one of our generated names
int u = name.lastIndexOf('_');
if (u >= 0) {
try {
if (Integer.valueOf(name.substring(u + 1, name.length())) > 0)
// it can, so transform it so that it cannot
name += "_1";
}
catch (NumberFormatException e) {
// not a number, so cannot be the same as one of our generated names
}
}
}
else
name = localName + "_" + ++i;
localNamePatternCount.put(localName, i);
}
else
name = "_" + ++otherPatternCount;
patternList.add(p);
patternNameMap.put(p, name);
}
return name;
}
private void startElement(String name) {
closeStartTag();
indent(level);
write('<');
write(name);
push(name);
startTagOpen = true;
level++;
}
private void closeStartTag() {
if (startTagOpen) {
startTagOpen = false;
write('>');
}
}
private void attribute(String name, String value) {
write(' ');
write(name);
write('=');
write('"');
chars(value, true);
write('"');
}
private void data(String str) {
if (str.length() > 0) {
closeStartTag();
chars(str, false);
suppressIndent = true;
}
}
private void chars(String str, boolean isAttribute) {
int len = str.length();
for (int i = 0; i < len; i++) {
char c = str.charAt(i);
switch (c) {
case '&':
write("&");
break;
case '<':
write("<");
break;
case '>':
write(">");
break;
case 0xD:
write("
");
break;
case 0xA:
if (isAttribute)
write("
");
else
write(c);
break;
case 0x9:
if (isAttribute)
write(" ");
else
write(c);
break;
case '"':
if (isAttribute)
write(""");
else
write(c);
break;
default:
write(c);
break;
}
}
}
private void endElement() {
--level;
if (startTagOpen) {
startTagOpen = false;
write("/>");
pop();
}
else {
if (!suppressIndent)
indent(level);
write("");
write(pop());
write(">");
}
suppressIndent = false;
}
private void indent(int level) {
write('\n');
for (int i = 0; i < level; i++)
write(" ");
}
private void write(String str) {
buf.append(str);
}
private void write(char c) {
buf.append(c);
}
private void push(String s) {
tagStack.add(s);
}
private String pop() {
return tagStack.remove(tagStack.size() - 1);
}
class Dumper implements PatternFunction {
public VoidValue caseEmpty(EmptyPattern p) {
startElement("empty");
endElement();
return VoidValue.VOID;
}
public VoidValue caseNotAllowed(NotAllowedPattern p) {
startElement("notAllowed");
endElement();
return VoidValue.VOID;
}
public VoidValue caseGroup(GroupPattern p) {
startElement("group");
p.getOperand1().apply(groupDumper);
p.getOperand2().apply(groupDumper);
endElement();
return VoidValue.VOID;
}
public VoidValue caseInterleave(InterleavePattern p) {
startElement("interleave");
p.getOperand1().apply(interleaveDumper);
p.getOperand2().apply(interleaveDumper);
endElement();
return VoidValue.VOID;
}
public VoidValue caseChoice(ChoicePattern p) {
final Pattern p1 = p.getOperand1();
final Pattern p2 = p.getOperand2();
if (p1 instanceof EmptyPattern)
p2.apply(optionalDumper);
else if (p2 instanceof EmptyPattern)
p1.apply(optionalDumper);
else
choice(p1, p2);
return VoidValue.VOID;
}
protected void choice(Pattern p1, Pattern p2) {
startElement("choice");
p1.apply(choiceDumper);
p2.apply(choiceDumper);
endElement();
}
public VoidValue caseOneOrMore(OneOrMorePattern p) {
startElement("oneOrMore");
p.getOperand().apply(dumper);
endElement();
return VoidValue.VOID;
}
public VoidValue caseElement(ElementPattern p) {
startElement("ref");
attribute("name", getName(p));
endElement();
return VoidValue.VOID;
}
public VoidValue caseAttribute(AttributePattern p) {
startElement("attribute");
outputName(p.getNameClass());
p.getContent().apply(dumper);
endElement();
return VoidValue.VOID;
}
protected void outputName(NameClass nc) {
if (nc instanceof SimpleNameClass) {
Name name = ((SimpleNameClass)nc).getName();
attribute("name", name.getLocalName());
attribute("ns", name.getNamespaceUri());
}
else
nc.accept(nameClassDumper);
}
public VoidValue caseData(DataPattern p) {
startData(p);
endElement();
return VoidValue.VOID;
}
private void startData(DataPattern p) {
startElement("data");
final Name dtName = p.getDatatypeName();
attribute("type", dtName.getLocalName());
attribute("datatypeLibrary", dtName.getNamespaceUri());
for (Iterator iter = p.getParams().iterator(); iter.hasNext();) {
startElement("param");
attribute("name", iter.next());
data(iter.next());
endElement();
}
}
public VoidValue caseDataExcept(DataExceptPattern p) {
startData(p);
startElement("except");
p.getExcept().apply(dumper);
endElement();
endElement();
return VoidValue.VOID;
}
public VoidValue caseValue(ValuePattern p) {
startElement("value");
Name dtName = p.getDatatypeName();
attribute("type", dtName.getLocalName());
attribute("datatypeLibrary", dtName.getNamespaceUri());
String stringValue = p.getStringValue();
final Object value = p.getValue();
String ns = "";
// XXX won't work with a datatypeLibrary that doesn't use Name to implement QName's
if (value instanceof Name) {
ns = ((Name)value).getNamespaceUri();
int colonIndex = stringValue.indexOf(':');
if (colonIndex < 0)
stringValue = stringValue.substring(colonIndex + 1, stringValue.length());
}
attribute("ns", ns);
data(stringValue);
endElement();
return VoidValue.VOID;
}
public VoidValue caseText(TextPattern p) {
startElement("text");
endElement();
return VoidValue.VOID;
}
public VoidValue caseList(ListPattern p) {
startElement("list");
p.getOperand().apply(dumper);
endElement();
return VoidValue.VOID;
}
public VoidValue caseRef(RefPattern p) {
return p.getPattern().apply(this);
}
public VoidValue caseAfter(AfterPattern p) {
startElement("i:after");
attribute("xmlns:i", INTERNAL_NAMESPACE);
p.getOperand1().apply(this);
p.getOperand2().apply(this);
endElement();
return VoidValue.VOID;
}
public VoidValue caseError(ErrorPattern p) {
startElement("i:error");
attribute("xmlns:i", INTERNAL_NAMESPACE);
endElement();
return VoidValue.VOID;
}
}
class ElementDumper extends Dumper {
public VoidValue caseElement(ElementPattern p) {
startElement("element");
outputName(p.getNameClass());
p.getContent().apply(dumper);
endElement();
return VoidValue.VOID;
}
}
class OptionalDumper extends AbstractPatternFunction {
public VoidValue caseOther(Pattern p) {
startElement("optional");
p.apply(dumper);
endElement();
return VoidValue.VOID;
}
public VoidValue caseOneOrMore(OneOrMorePattern p) {
startElement("zeroOrMore");
p.getOperand().apply(dumper);
endElement();
return VoidValue.VOID;
}
}
class GroupDumper extends Dumper {
public VoidValue caseGroup(GroupPattern p) {
p.getOperand1().apply(this);
p.getOperand2().apply(this);
return VoidValue.VOID;
}
}
class ChoiceDumper extends Dumper {
protected void choice(Pattern p1, Pattern p2) {
p1.apply(this);
p2.apply(this);
}
}
class InterleaveDumper extends Dumper {
public VoidValue caseInterleave(InterleavePattern p) {
p.getOperand1().apply(this);
p.getOperand2().apply(this);
return VoidValue.VOID;
}
}
class NameClassDumper implements NameClassVisitor {
public void visitChoice(NameClass nc1, NameClass nc2) {
startElement("choice");
nc1.accept(choiceNameClassDumper);
nc2.accept(choiceNameClassDumper);
endElement();
}
public void visitNsName(String ns) {
startElement("nsName");
attribute("ns", ns);
endElement();
}
public void visitNsNameExcept(String ns, NameClass nc) {
startElement("nsName");
attribute("ns", ns);
startElement("except");
nc.accept(nameClassDumper);
endElement();
endElement();
}
public void visitAnyName() {
startElement("anyName");
endElement();
}
public void visitAnyNameExcept(NameClass nc) {
startElement("anyName");
startElement("except");
nc.accept(nameClassDumper);
endElement();
endElement();
}
public void visitName(Name name) {
startElement("name");
attribute("ns", name.getNamespaceUri());
data(name.getLocalName());
endElement();
}
public void visitError() {
startElement("error");
endElement();
}
public void visitNull() {
visitAnyName();
}
}
class ChoiceNameClassDumper extends NameClassDumper {
public void visitChoice(NameClass nc1, NameClass nc2) {
nc1.accept(this);
nc2.accept(this);
}
}
}