com.sun.msv.generator.Generator Maven / Gradle / Ivy
/*
* @(#)$Id: Generator.java 1478 2002-12-23 23:17:33Z kk122374 $
*
* Copyright 2001 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the proprietary information of Sun Microsystems, Inc.
* Use is subject to license terms.
*
*/
package com.sun.msv.generator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;
import org.relaxng.datatype.Datatype;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.Text;
import com.sun.msv.datatype.xsd.NmtokenType;
import com.sun.msv.datatype.xsd.StringType;
import com.sun.msv.datatype.xsd.XSDatatype;
import com.sun.msv.grammar.AttributeExp;
import com.sun.msv.grammar.BinaryExp;
import com.sun.msv.grammar.ChoiceExp;
import com.sun.msv.grammar.ConcurExp;
import com.sun.msv.grammar.DataExp;
import com.sun.msv.grammar.ElementExp;
import com.sun.msv.grammar.Expression;
import com.sun.msv.grammar.ExpressionPool;
import com.sun.msv.grammar.ExpressionVisitorVoid;
import com.sun.msv.grammar.InterleaveExp;
import com.sun.msv.grammar.ListExp;
import com.sun.msv.grammar.MixedExp;
import com.sun.msv.grammar.NameClass;
import com.sun.msv.grammar.OneOrMoreExp;
import com.sun.msv.grammar.OtherExp;
import com.sun.msv.grammar.ReferenceExp;
import com.sun.msv.grammar.SequenceExp;
import com.sun.msv.grammar.ValueExp;
import com.sun.msv.grammar.util.ExpressionPrinter;
import com.sun.msv.util.StringPair;
import com.sun.xml.util.XmlChars;
/**
* generates an XML DOM instance that conforms to the given schema.
*
* @author Kohsuke KAWAGUCHI
*/
public class Generator implements ExpressionVisitorVoid {
/** generation parameters */
private final GeneratorOption opts;
private final ExpressionPool pool;
private final Document domDoc;
/** current generated node */
private Node node;
/** current nest level (depth of elements). */
private int depth = 0;
/** this flag is set to true once an error is generated. */
private boolean errorGenerated = false;
/** returns true if generator should cut back. */
protected boolean cutBack() { return depth>5; }
/** ID tokens that are used */
private final Set ids = new HashSet();
/** Text nodes of IDREFs that should be "patched" by IDs. */
private final Set idrefs = new HashSet();
/** all ElementExps in the grammar. */
private final ElementExp[] elementDecls;
/** all AttributeExps in the grammar. */
private final AttributeExp[] attributeDecls;
/** generates instance by using default settings. */
public static void generate( Expression exp, Document emptyDoc ) {
generate( exp, emptyDoc, new GeneratorOption() );
}
/** generates instance by custom settings. */
public static void generate( Expression exp, Document emptyDoc, GeneratorOption opts ) {
Generator g;
for( int i=0; i<10; i++ ) {
// make it empty.
while( emptyDoc.hasChildNodes())
emptyDoc.removeChild(emptyDoc.getFirstChild());
do {
while( emptyDoc.getFirstChild()!=null ) // delete any existing children
emptyDoc.removeChild( emptyDoc.getFirstChild() );
g = new Generator(exp,emptyDoc,opts);
exp.visit(g);
// if error ratio is specified and no error is generated, do it again.
}while( !g.errorGenerated && opts.errorSpecified() );
Object[] ids = g.ids.toArray();
if( ids.length==0 && g.idrefs.size()!=0 )
continue; // IDREF is generated but no ID is generated.
// try again.
// patch IDREF.
Iterator itr = g.idrefs.iterator();
while( itr.hasNext() ) {
Text node = (Text)itr.next();
node.setData( (String)ids[opts.random.nextInt(ids.length)] );
}
return;
}
throw new Error("no ID");
}
protected Generator( Expression exp, Document emptyDoc, GeneratorOption opts ) {
opts.fillInByDefault();
this.opts = opts;
this.pool = opts.pool;
node = domDoc = emptyDoc;
// collect element and attribute decls.
Set[] s= ElementDeclCollector.collect(exp);
elementDecls = new ElementExp[s[0].size()];
s[0].toArray(elementDecls);
attributeDecls = new AttributeExp[s[1].size()];
s[1].toArray(attributeDecls);
}
/** annotate DOM by adding a comment that an error is generated. */
private void noteError( String error ) {
errorGenerated = true;
if( !opts.insertComment ) return;
Node com = domDoc.createComment(" "+error+" ");
Node n = node;
if( n.getNodeType()==Node.ATTRIBUTE_NODE ) {
n = ((Attr)n).getOwnerElement();
n.insertBefore( com, n.getFirstChild() );
} else {
n.appendChild(com);
}
}
public void onEpsilon() {}
public void onNullSet() { throw new Error(); } // assertion failed
public void onSequence( SequenceExp exp ) {
if(!(exp.exp1 instanceof AttributeExp)
&& !(exp.exp2 instanceof AttributeExp) ) {
// sequencing error of attribute is meaningless.
if( opts.random.nextDouble() < opts.probSeqError ) {
// generate sequencing error
noteError("swap sequence to "+
ExpressionPrinter.printSmallest(exp.exp2)+","+
ExpressionPrinter.printSmallest(exp.exp1) );
exp.exp2.visit(this);
exp.exp1.visit(this);
return;
}
}
// generate valid instance.
exp.exp1.visit(this);
exp.exp2.visit(this);
}
public void onInterleave( InterleaveExp ip ) {
// collect children
Vector vec = getChildren(ip);
Node old = node;
// generate XML fragment for each child.
for( int i=0; i