All Downloads are FREE. Search and download functionalities are using the official Maven repository.

toxgene.core.parser.ToxTemplate Maven / Gradle / Ivy

The newest version!
/**
 * Parses a Tox-template document; creates the corresponding objects
 * for dealing with the constructs in the template.
 * 
 * @author Denilson Barbosa
 * @version 0.1
 */

package toxgene.core.parser;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import java.util.Vector;
import java.io.InputStream;

import toxgene.core.Engine;
import toxgene.core.ToXgeneErrorException;
import toxgene.core.XML.XMLSchema;
import toxgene.core.factories.*;
import toxgene.core.genes.CreateGeneException;
import toxgene.core.genes.Gene;
import toxgene.core.genes.lists.ToxList;
import toxgene.core.genes.lists.WhereClause;
import toxgene.core.genes.literals.*;
import toxgene.core.genes.trees.*;
import toxgene.core.random.*;
import toxgene.interfaces.ToXgeneReporter;
import toxgene.util.Dictionary;
import toxgene.util.DuplicateKeyException;
import toxgene.util.KeyNotFoundException;

public class ToxTemplate {
	/**
	 * This is the engine that runs one session of ToXgene. It contains
	 * all data necessary for producing the output.
	 */
	private Engine tgEngine;

	private Dictionary simpleTypes, complexTypes, toxLists, toxFiles;

	private Dictionary randomGenerators, simpleTypeFactories;

	/**
	 * Contains the recursive descendants of a recursive element (i.e.,
	 * descendants with the same name as the recursive ancestor).
	 */
	private Vector recChildren;

	private ToxValidatingParser parser;

	public ToxTemplate(Engine engine, ToXgeneReporter reporter) {
		tgEngine = engine;
	}

	public void buildTemplate(InputStream uri){
		Document doc = null;
		Node template, node;
		String node_name;

		try {
			//we use this toxgene.core.parser class which validates the document againts the
			//tox-gene DTD and trims the unnecessary #text elements
			parser = new ToxValidatingParser(true, tgEngine.getToXgeneReporter());
			doc = parser.parse(uri, true);
		} catch (Exception e) {
			throw new ToXgeneErrorException("template file could not be parsed: \n"
																			+ e.getMessage());
		}
		tgEngine.getToXgeneReporter().explain(uri + " parsed without problems.\n");

		//Create the map for the simpleType factory objects
		simpleTypeFactories = new Dictionary();
		try {
			simpleTypeFactories.add("string", new StringFactory(tgEngine));
			simpleTypeFactories.add("integer", new IntegerFactory(
					XMLSchema.INTEGER, tgEngine));
			simpleTypeFactories.add("long", new IntegerFactory(XMLSchema.LONG,
																												 tgEngine));
			simpleTypeFactories.add("unsignedLong", new IntegerFactory(
					XMLSchema.UNSIGNED_LONG, tgEngine));
			simpleTypeFactories.add("int", new IntegerFactory(XMLSchema.INT,
																												tgEngine));
			simpleTypeFactories.add("unsignedInt", new IntegerFactory(
					XMLSchema.UNSIGNED_INT, tgEngine));
			simpleTypeFactories.add("short",new IntegerFactory(XMLSchema.SHORT,
																												 tgEngine));
			simpleTypeFactories.add("unsignedShort", new IntegerFactory(
					XMLSchema.SHORT, tgEngine));
			simpleTypeFactories.add("byte", new IntegerFactory(XMLSchema.BYTE,
																												 tgEngine));
			simpleTypeFactories.add("nonNegativeInteger", new IntegerFactory(
					XMLSchema.NON_NEGATIVE_INTEGER, tgEngine));
			simpleTypeFactories.add("positiveInteger", new IntegerFactory(
					XMLSchema.POSITIVE_INTEGER, tgEngine));
			simpleTypeFactories.add("nonPositiveInteger", new IntegerFactory(
					XMLSchema.NON_POSITIVE_INTEGER, tgEngine));
			simpleTypeFactories.add("negativeInteger", new IntegerFactory(
					XMLSchema.NEGATIVE_INTEGER, tgEngine));
			simpleTypeFactories.add("decimal", new RealFactory(
					XMLSchema.DECIMAL, tgEngine));
			simpleTypeFactories.add("float", new RealFactory(XMLSchema.FLOAT,
																											 tgEngine));
			simpleTypeFactories
					.add("double", new RealFactory(XMLSchema.DOUBLE, tgEngine));
			simpleTypeFactories.add("date", new DateFactory(tgEngine));
			//for compatibility reasons only
			simpleTypeFactories.add("number", new RealFactory(XMLSchema.FLOAT,
																												tgEngine));
		} catch (DuplicateKeyException e) {
		}

		//Create the maps for the types, lists, files...
		simpleTypes = tgEngine.simpleTypes;
		complexTypes = new Dictionary();
		toxLists = tgEngine.toxLists;
		toxFiles = tgEngine.toxFiles;
		randomGenerators = tgEngine.randomGenerators;

		template = doc.getDocumentElement();
		NodeList children = template.getChildNodes();
		for (int i = 0; i < children.getLength(); i++) {
			node = children.item(i);
			node_name = node.getNodeName();

			if (node_name.compareTo("tox-distribution") == 0) {
				processDistribution(node);
				continue;
			}
			if (node_name.compareTo("simpleType") == 0) {
				processSimpleType(node);
				continue;
			}

			if (node_name.compareTo("complexType") == 0) {
				processComplexType(node);
				continue;
			}

			if (node_name.compareTo("tox-list") == 0) {
				processToxList(node);
				continue;
			}

			if (node_name.compareTo("tox-document") == 0) {
				processToxFile(node);
			}
		}

		//We're done.
		tgEngine.getToXgeneReporter().explain("DONE processing template file.");
		//	doc = null;
	};

	public int findElement(Node node) {
		try{
			return parser.findElement(node);
		}catch (Exception e){
			return 0;
		}
	}

	public Vector documents() {
		return toxFiles.values();
	}

	public Vector lists() {
		return toxLists.values();
	}

	public void deleteList(String name) {
		try {
			toxLists.remove(name);
		} catch (KeyNotFoundException e) {
			throw new ToXgeneErrorException("DIED A REALLY HORRIBLE DEATH");
		}
	}

	/**
	 * Processes a named simpleType declaration.
	 */
	private void processSimpleType(Node node) {
		Gene gene = null;
		String name = "";
		//SimpleType elements have only the 'name' attribute
		try {
			name = ((Element) node).getAttributeNode("name").getNodeValue();
		} catch (NullPointerException e) {
			throw new ToXgeneErrorException("\"name\" attribute required for "
																			+"simpleType.",findElement(node));
		}

		if (simpleTypes.contains(name)) {
			throw new ToXgeneErrorException("simpleType \"" + name
																			+ "\" already declared!",
																			findElement(node));
		}

		Node restriction = ((Element) node).getFirstChild();

		tgEngine.getToXgeneReporter().explain("Processing SimpleType: " + name);

		//invokes the method for creating the gene for this type

		if ((((Element) restriction).getElementsByTagName("tox-expr"))
				.getLength() != 0) {
			//this simple type has a scan/sample descendant
			Node scanNode = restriction.getFirstChild();
			String nodeName = scanNode.getNodeName();
			if ((nodeName.compareTo("tox-scan") != 0)
					&& (nodeName.compareTo("tox-sample") != 0)
					&& (nodeName.compareTo("tox-foreach") != 0)) {
				throw new ToXgeneErrorException("invalid simpleType declaration",
											 findElement(node));
			}
			gene = createToxScanGene(scanNode, null, true, null, 1, 1);
		} else {
			gene = createSimpleTypeGene(restriction);
		}

		//registers the gene in the list.
		try {
			simpleTypes.add(name, gene);
		} catch (DuplicateKeyException e1) {
		}

		tgEngine.getToXgeneReporter().explain(" -----> done!");

	}

	/**
	 * Processes a named complex type declaration
	 */
	private void processComplexType(Node node) {
		ToxComplexType complex;

		//complexType elements have only a 'name' attribute
		String name = ((Element) node).getAttributeNode("name").getNodeValue();
		tgEngine.getToXgeneReporter().explain("Processing ComplexType: " + name);

		String mixed = ((Element) node).getAttributeNode("mixed")
				.getNodeValue();

		//invokes the actual constructor for complexType genes. Null is used
		//here because this complextype is not a descendant of any ToxScan node

		if (mixed.compareTo("true") == 0) {
			complex = createComplexType(node.getChildNodes(), null, null, true,
					0, 0);
		} else {
			complex = createComplexType(node.getChildNodes(), null, null,
					false, 0, 0);
		}

		try {
			complexTypes.add(name, complex);
		} catch (DuplicateKeyException e2) {
			throw new ToXgeneErrorException("duplicate type declaration: " + name,
										 findElement(node));
		}
		tgEngine.getToXgeneReporter().explain("-----> done ComplexType: " + name);
	}

	/**
	 * Processes a tox-list declaration.
	 */
	private void processToxList(Node node) {
		ToxList list = null;

		//tox-lists have at least the 'name' attribute.
		String name = ((Element) node).getAttributeNode("name").getNodeValue();

		tgEngine.getToXgeneReporter().explain("Processing tox-list: " + name);

		//The 'unique' attribute is optional
		String unique = "";
		try {
			unique = ((Element) node).getAttributeNode("unique").getNodeValue();
		} catch (NullPointerException e) {
		}

		//The 'where' attribute is optional
		String where = "";
		try {
			where = ((Element) node).getAttributeNode("where").getNodeValue();
		} catch (NullPointerException e) {
		}

		//the 'dump' attribute is mandatory (yes|no)
		String dump = ((Element) node).getAttributeNode("dump").getNodeValue();

		//get the gene for this list
		Node root = node.getFirstChild();
		ToxElement mygene = (ToxElement) createToxElementGene(root, null, null);

		try {
			String readFrom = ((Element) node).getAttributeNode("readFrom")
					.getNodeValue();
			String abort = ((Element) node).getAttributeNode("abort")
					.getNodeValue();

			list = new ToxList(name, mygene, unique, where,
												 (dump.compareTo("yes") == 0), readFrom,
												 (abort.compareTo("yes") == 0), findElement(node),
												 tgEngine);
		} catch (NullPointerException e1) {
			list = new ToxList(name, mygene, unique, where,
												 (dump.compareTo("yes") == 0), findElement(node),
												 tgEngine);
		}

		//registers the list in the system.
		try {
			toxLists.add(name, list);
		} catch (DuplicateKeyException e3) {
			throw new ToXgeneErrorException("duplicate tox-list declaration: "
																			+ e3.getMessage(),findElement(node));
		}

		//gets all lists referenced by this list gene
		Vector references = getListReferences(mygene);
		if (references != null) {
			list.references(references);
		}

		tgEngine.getToXgeneReporter().explain("-----> done tox-list: " + name);
	}

	/**
	 * Processes a tox-file declaration
	 */
	private void processToxFile(Node node) {
		ToxCollection file = null;
		int copies = 0, start = 0;

		//ToxFile elements have 'name' and 'copies' attributes
		String name = ((Element) node).getAttributeNode("name").getNodeValue();

		try {
			copies = Integer.parseInt(((Element) node).getAttributeNode(
					"copies").getNodeValue());
		} catch (NumberFormatException e) {
			throw new ToXgeneErrorException("invalid number of copies for file "
																			+ name, findElement(node));
		}
		if (copies < 1) {
			copies = 1;
			tgEngine.getToXgeneReporter().warning("number of copies is smaller than 1!\n"
																				+ "\tdefining copies = 1");
		}

		tgEngine.getToXgeneReporter().explain("Processing document: " + name);

		//Let's get the root element in this document
		Node root = ((Element) node).getFirstChild();
		ToxElement gene = (ToxElement) createToxElementGene(root, null, null);

		//check the starting number for indexing the files
		try {
			start = Integer.parseInt(((Element) node).getAttributeNode(
					"starting-number").getNodeValue());
		} catch (NumberFormatException e) {
			throw new ToXgeneErrorException("invalid starting number for "
																			+"tox-document:" + name,
																			findElement(node));
		} catch (NullPointerException e1) {
			start = 0;
		}
		if (start < 0) {
			start = 0;
			tgEngine.getToXgeneReporter().warning("starting-number is negative! \n"
																				+ "\tdefining starting-number = 0");
		}

		//check whether we should pad leading 0's for the files names
		boolean pad;
		try {
			pad = ((((Element) node).getAttributeNode("pad").getNodeValue())
					.compareTo("yes") == 0);
		} catch (NullPointerException e) {
			pad = false;
			tgEngine.getToXgeneReporter().warning("It seems you are using an outdated "
												 +"DTD for ToXgene templates. Please "
												 +"download the latest one.");
		}

		//check whether this toxFile has a DTD attached to it
		try {
			String dtd = ((Element) node).getAttributeNode("DTD-file")
					.getNodeValue();

			if (copies == 1)
				file = new ToxCollection(name, gene, tgEngine.version());
			else
				file = new ToxCollection(name, copies, start, dtd, gene, pad,
												 tgEngine.version());
		} catch (NullPointerException e) {
			if (copies == 1)
				file = new ToxCollection(name, gene, tgEngine.version());
			else
				file = new ToxCollection(name, copies, start, gene, pad,
																 tgEngine.version());

		}

		//register this tox-file in the list
		try {
			toxFiles.add(name, file);
		} catch (DuplicateKeyException e2) {
			throw new ToXgeneErrorException("duplicate document declaration: "
																			+ e2.getMessage(),
																			findElement(node));
		}

		//gets all lists referenced by this list gene
		Vector references = getListReferences(gene);
		if (references != null) {
			file.references(references);
		}

		tgEngine.getToXgeneReporter().explain("-----> done tox-document: " + name);
	}

	/**
	 * Creates a new ToxRandom generator according to the probability
	 * distribution specified.
	 */
	private void processDistribution(Node node) {
		ToxRandom rand = null;

		//name, type, min and max are mandatory:
		String name = ((Element) node).getAttributeNode("name").getNodeValue();
		String type = ((Element) node).getAttributeNode("type").getNodeValue();

		float min = (new Float(((Element) node)
				.getAttributeNode("minInclusive").getNodeValue())).floatValue();
		float max = (new Float(((Element) node)
				.getAttributeNode("maxInclusive").getNodeValue())).floatValue();

		if (type.compareTo("uniform") == 0) {
			rand = tgEngine.newUniformDistribution(min, max);
		}

		if (type.compareTo("normal") == 0) {
			try {
				float mean = (new Float(((Element) node).getAttributeNode(
						"mean").getNodeValue())).floatValue();
				float var = (new Float(((Element) node).getAttributeNode(
						"variance").getNodeValue())).floatValue();
				rand = tgEngine.newGaussianDistribution(min, max, mean, var);
			} catch (NullPointerException e1a) {
				throw new ToXgeneErrorException("Invalid gaussian distribution "
																				+"specification!",findElement(node));
			}
			catch (CreateGeneException cg1){
				throw new ToXgeneErrorException(cg1.getMessage(),findElement(node));
			}
		}

		if (type.compareTo("lognormal") == 0) {
			try {
				float mean = (new Float(((Element) node).getAttributeNode(
						"mean").getNodeValue())).floatValue();
				float var = (new Float(((Element) node).getAttributeNode(
						"variance").getNodeValue())).floatValue();
				rand = tgEngine.newLogNormalDistribution(min, max, mean, var);
			} catch (NullPointerException e1) {
				throw new ToXgeneErrorException("Invalid lognormal distribution "
																				+"specification!",findElement(node));
			}
			catch (CreateGeneException cg1){
				throw new ToXgeneErrorException(cg1.getMessage(),findElement(node));
			}
		}

		if (type.compareTo("exponential") == 0) {
			try {
				float mean = (new Float(((Element) node).getAttributeNode(
						"mean").getNodeValue())).floatValue();
				rand = tgEngine.newExponentialDistribution(min, max, mean);
			} catch (NullPointerException e1) {
				throw new ToXgeneErrorException("Invalid exponential distribution "
																				+"specification!",findElement(node));
			}
			catch (CreateGeneException cg1){
				throw new ToXgeneErrorException(cg1.getMessage(),findElement(node));
			}
		}

		if (type.compareTo("geometric") == 0) {
			try {
				float mean = (new Float(((Element) node).getAttributeNode(
						"mean").getNodeValue())).floatValue();
				rand = tgEngine.newGeometricDistribution(min, max, mean);
			} catch (NullPointerException e1) {
				throw new ToXgeneErrorException("Invalid geometric distribution "
																				+"specification!",findElement(node));
			}
			catch (CreateGeneException cg1){
				throw new ToXgeneErrorException(cg1.getMessage(),findElement(node));
			}
		}

		if (type.compareTo("constant") == 0) {
			if (min != max) {
				tgEngine.getToXgeneReporter().warning("different minInclusive and "
																							+"maxInclusive values "
																							+"for constant\ndistribution \""
																							+name
																							+"\" (line "+findElement(node)
																							+"). Using minInclusive.");
																					
			}
			try{
				rand = tgEngine.newConstantDistribution(min);
			}
			catch (CreateGeneException cg1){
				throw new ToXgeneErrorException(cg1.getMessage(),findElement(node));
			}
		}

		if (type.compareTo("user-defined") == 0) {
			rand = new ToxUserDefined(min, max, tgEngine.nextRandomSeed());
			NodeList children = node.getChildNodes();

			float sum = 0;
			for (int i = 0; i < children.getLength(); i++) {
				Element element = (Element) children.item(i);
				float value = 0, percent = 0;
				try {
					value = Float.parseFloat(element.getAttributeNode("value").
																	 getNodeValue());
					percent = Float.parseFloat(element.getAttributeNode("tox-percent").
																		 getNodeValue());
				} catch (NumberFormatException e1) {
					throw new ToXgeneErrorException(
							"could not parse floating point numbers for user "
							+ "defined-distribution" + name, findElement(node));
				}
				
				sum += percent;
				if (sum > 100.0) {
					throw new ToXgeneErrorException("probability mass adds to more than "
																					+"100!",findElement(node));
				} else {
					((ToxUserDefined) rand).addOption(value, percent);
				}
			}
		}

		//register this random generator
		try {
			randomGenerators.add(name, rand);
		} catch (DuplicateKeyException e2) {
			//we know this can't happen since name is an ID for the element!
		}
	}

	/**
	 * Extracts all lists that are referenced in the given gene. This
	 * information is used for garbage collection purposes. This function is
	 * only called from processToxList and processToxFile, thus we can pass a
	 * ToxElement along.
	 */
	private Vector getListReferences(TreeGene gene) {
		Vector result = null;

		Vector children = gene.children();
		int size = children == null ? 0 : children.size();
		for (int i = 0; i < size; i++) {
			try {
				TreeGene temp = (TreeGene) children.get(i);
				if (temp instanceof ToxScan) {
					if (result == null) {
						result = new Vector();
					}
					result.add(((ToxScan) temp).list());
				}

				Vector temp2 = getListReferences(temp);
				if (temp2 != null) {
					if (result == null) {
						result = new Vector();
					}
					result.addAll(temp2);
				}
			} catch (ClassCastException e) {
				//this will occur with literal Genes
				continue;
			}
		}
		return result;
	}

	/**
	 * This method creates a new Gene from a simpleType definition.
	 */
	private Gene createSimpleTypeGene(Node restriction) {
		String base;
		SimpleTypeFactory factory;

		//find out the base type for this gene
		base = ((Element) restriction).getAttributeNode("base").getNodeValue();

		try {
			//use the proper gene factory
			factory = (SimpleTypeFactory) simpleTypeFactories.get(base);			
			try {
				return (factory.createGene(restriction.getChildNodes()));
			}
			catch (CreateGeneException e) {
				throw new ToXgeneErrorException("invalid simpleType defintion: " +
																				e.getMessage(),
																				findElement(restriction));
			}
		}
		catch (KeyNotFoundException e1) {
			throw new ToXgeneErrorException("cannot find type \"" + base
																			+ "\" definition",
																			findElement(restriction));
		}
	}

	/**
	 * Processes the three primitive genes in ToXgene: tox-string, tox-number
	 * and tox-value. Throws an exception if the gene is not recognized.
	 * 
	 * Primitive nodes have no other genes as their descendants, so we do not
	 * care about tox-lookups here (the calling method will do that).
	 */
	private Gene createPrimitiveGene(Node node) throws Exception {
		String content = node.getNodeName();
		String base = "string"; //this is the default factory to be used
		SimpleTypeFactory factory;

		if (content.compareTo("tox-number") == 0) {
			base = "float";
		}

		try {
			//use the proper gene factory
			factory = (SimpleTypeFactory) simpleTypeFactories.get(base);
			return (factory.createGene(node));
		} catch (KeyNotFoundException e) {
		}

		return null;
	}

	/**
	 * Creates a container for a complex type definition taking lookup and
	 * lookup items into consideration.
	 * 
	 * nodes is the content of the complex type. lookup is the closest lookup
	 * ancestor (if any), for which all lookupitems refer to. recursive is the
	 * closest recursively defined ancestor (if any) hasMixedContent specifies
	 * whether mixed content is allowed in this type min_qtty and max_qtty are
	 * the minimum and maximum number of occurrences for the closest ancestor of
	 * this complexType
	 */
	private ToxComplexType createComplexType(NodeList nodes, ToxScan scan,
			String recursive, boolean hasMixedContent, int min_qtty,
			int max_qtty) {
		ToxComplexType holder = new ToxComplexType();
		String node_name, name, type;
		Gene gene;
		/**
		 * determines whether the complex object has CDATA content
		 */
		boolean hasCDATA = false;
		/**
		 * determines whether the complex object has sub-elements
		 */
		boolean hasElement = false;

		//scan through all elements in this complex type definition at the
		// first
		//level only.
		for (int i = 0; i < nodes.getLength(); i++) {
			Node node = nodes.item(i);
			node_name = node.getNodeName();

			if (node_name.compareTo("attribute") == 0) {
				gene = createToxAttributeGene(node, scan);
				holder.addAttribute(gene);
				continue;
			}

			if (node_name.compareTo("element") == 0) {
				//consistency check
				if (!hasMixedContent && hasCDATA) {
					throw new ToXgeneErrorException("mixed content not allowed for "
							+ "element in complex type declaration!", findElement(node));
				}

				//createToxElementGene does all the job for us here
				gene = createToxElementGene(node, scan, recursive);
				holder.addContent(gene);

				hasElement = true;
				continue;
			}

			if (node_name.compareTo("tox-scan") == 0) {
				ToxScan myScan = createToxScanGene(node, scan, hasMixedContent,
						recursive, min_qtty, max_qtty);

				if (myScan.hasAttributes()) {
					holder.addContainer(myScan);
				}
				holder.addScan(myScan);

				continue;
			}

			if (node_name.compareTo("tox-sample") == 0) {
				ToxScan myScan = createToxScanGene(node, scan, hasMixedContent,
						recursive, min_qtty, max_qtty);

				if (myScan.hasAttributes()) {
					holder.addContainer(myScan);
				}
				holder.addScan(myScan);

				continue;
			}

			if (node_name.compareTo("tox-foreach") == 0) {
				ToxScan myScan = createToxScanGene(node, scan, hasMixedContent,
						recursive, min_qtty, max_qtty);

				if (myScan.hasAttributes()) {
					holder.addContainer(myScan);
				}
				holder.addScan(myScan);

				continue;
			}

			if (node_name.compareTo("tox-alternatives") == 0) {
				ToxAlternatives alt = createToxAlternativesGene(node, scan,
						recursive, hasMixedContent, min_qtty, max_qtty);
				if (alt.hasAttributes()) {
					holder.addContainer(alt);
				}
				holder.addContent(alt);
				continue;
			}

			if (node_name.compareTo("tox-if") == 0) {
				ToxIf ifgene = createToxIfGene(node, scan, recursive,
						hasMixedContent, min_qtty, max_qtty);
				if (ifgene.hasAttributes()) {
					holder.addContainer(ifgene);
				}
				holder.addContent(ifgene);
				continue;
			}

			if (node_name.compareTo("tox-expr") == 0) {
				ToxGet tget = createToxGetGene(node, scan, min_qtty, max_qtty);
				holder.addContent(tget);
				if (tget.getType().compareTo("complex") != 0) {
					hasCDATA = true;
				}
				continue;
			}

			// a last resource: this could be a primitive gene or a lookup-item
			// which do not have to know about tox-lookups!!!

			try {
				gene = createPrimitiveGene(node);
				holder.addContent(gene);
				hasCDATA = true;
				continue;
			} catch (Exception e) {
				throw new ToXgeneErrorException("invalid type declaration!",
																				findElement(node));
			}
		}

		return holder;
	}

	/**
	 * Creates a Gene for a (simple or complex) element.
	 */
	private Gene createToxElementGene(Node element, ToxScan scan,
			String recursive) {
		String name, type, distribution = "", int_scan;
		int min_qtty = 0, max_qtty = 0, min_occurs = 0, max_occurs = 0;

		//this is the element to be returned
		ToxElement elementGene = null;

		//random generators are passed from parent to child nodes.
		ToxRandom myRandom = null;

		String myRecursive = recursive;

		//name, type, minOccurs and maxOccurs are mandatory
		name = new String(((Element) element).getAttributeNode("name")
				.getNodeValue());

		min_occurs = Integer.parseInt(((Element) element).getAttributeNode(
				"minOccurs").getNodeValue());

		int_scan = ((Element) element).getAttributeNode("maxOccurs")
				.getNodeValue();
		if (int_scan.compareTo("unbounded") != 0) {
			max_occurs = Integer.parseInt(int_scan);
		} else {
			max_occurs = -1;
		}

		tgEngine.getToXgeneReporter().explain("Processing element: " + name + ", minOccurs: "
				+ min_occurs + ", maxOccurs: " + max_occurs);

		//check whether the genes of this element have to be reset each time
		// the
		//gene is instantiated.
		boolean resetElement = false;
		try {
			String reset = ((Element) element).getAttributeNode("tox-reset")
					.getNodeValue();
			resetElement = (reset.compareTo("yes") == 0);
		} catch (Exception e) {
			tgEngine.getToXgeneReporter().warning("it seems your template referencess an outdated DTD.\n"
											 + "Some ToXgene features cannot work properly unless "
											 + "you use the latest DTD.\n");
		}

		/**
		 * tox-omitTag is used to indicate we are generating recursive mixe
		 * content.
		 */

		boolean omitTag = false;
		try {
			String reset = ((Element) element).getAttributeNode("tox-omitTag")
					.getNodeValue();
			omitTag = (reset.compareTo("yes") == 0);
		} catch (Exception e) {
			tgEngine.getToXgeneReporter().warning("it seems your template refers to an"
											 +" outdated DTD.\nSome ToXgene features "
											 +"will not work properly unless "
											 + "you use the latest DTD.\n");
		}

		/**
		 * The number of occurrences of an element is either randomly generated,
		 * for which we can : 1. use the one specified in a tox-distribution
		 * attribute 2. create one from min_qtty and max_qtty;
		 * 
		 * or it is "unbounded" (in this case we *require* both min_qtty and
		 * max_qtty to be defined): it will be read from a tox-list later on.
		 */

		try {
			distribution = ((Element) element).getAttributeNode(
					"tox-distribution").getNodeValue();
			myRandom = (ToxRandom) randomGenerators.get(distribution);

			min_qtty = (int) myRandom.minValue();
			max_qtty = (int) myRandom.maxValue();
		} catch (NullPointerException e4) {
			//this means the element has no random generator specified

			min_qtty = min_occurs;
			max_qtty = max_occurs;

			if (max_occurs > 0 && max_occurs < min_occurs) {
				max_occurs = min_occurs;
				tgEngine.getToXgeneReporter().warning("maxOccurs smaller than minOccurs "
																					+"for element: "+ name
																					+ ". Defining maxOccurs = "
																					+ min_occurs+ ".");
			}

			if (max_qtty != -1) {
				if (min_occurs == max_occurs) {
					//this is more common than we think
					myRandom = tgEngine.newConstantDistribution(min_occurs);
				} else {
					//this is the default case, uniform distribution
					myRandom = tgEngine.newUniformDistribution(min_occurs, max_occurs);
				}
			}
		} catch (KeyNotFoundException e3) {
			//this means that the tox-distribution is invalid
			throw new ToXgeneErrorException("undefined tox-distribution " +
																			distribution+ " in element\"" + name
																			+ "\"", findElement(element));
		}

		//simple integrity checks
		if (min_qtty < min_occurs) {
			throw new ToXgeneErrorException("element "+name+" cannot occur less "
																			+"than "+ min_occurs+ " times!",
																			findElement(element));
		}
		if (max_occurs != -1 && max_qtty > max_occurs) {
			tgEngine.getToXgeneReporter().warning("maxOccurs for element \"" + name
					+ "\" is smaller than "
					+ "value defined by maxInclusive in distribution "
					+ distribution);
		}

		// At this point we have to check whether this is a recursive
		// element. If so, the actual gene was processed before.

		ToxRecursiveElement recChild = null;
		boolean isRecChild = false;
		if ((recursive != null) && (name.compareTo(recursive) == 0)) {
			if (recChildren == null) {
				recChildren = new Vector();
			}
			recChild = new ToxRecursiveElement(min_qtty, max_qtty, myRandom,
																				 name);
			recChildren.add(recChild);
			isRecChild = true;
			
			tgEngine.getToXgeneReporter().explain("Processing recursive descendant of: " + name);
		}

		// Another option: this is the ancestor recursive element, we have to
		// keep its gene for when we get to the descendant, which is taken care
		// of with the previous code block
		boolean isRecAncestor = false;
		ToxRandom recLevels = null;
		String recD = null;
		try {
			recD = ((Element) element).getAttributeNode("tox-recursionLevels")
					.getNodeValue();

			tgEngine.getToXgeneReporter().explain("Processing recursive element: " + name);

			recLevels = (ToxRandom) tgEngine.randomGenerators.get(recD);

			//only one recursive element at a time
			if (myRecursive != null) {
				throw new ToXgeneErrorException("cannot have recursive element: "
																				+recD+" nested within other recursive"
																				+" element: "+ myRecursive,
																				findElement(element));
			}

			isRecAncestor = true;
			myRecursive = name;
			tgEngine.getToXgeneReporter().explain("-----> done recursive element");
		} catch (KeyNotFoundException e) {
			try {
				int l = Integer.parseInt(recD);
				recLevels = tgEngine.newUniformDistribution(0, l);
				isRecAncestor = true;
				myRecursive = name;

				tgEngine.getToXgeneReporter().explain("-----> done recursive element");
			} catch (NumberFormatException e1) {
				throw new ToXgeneErrorException("invalid recursive element \""
											+ name
											+ "\":\n cannot find tox-distribution named with or "
											+ "parse an integer from \"" + recD + "\"",
											findElement(element));
			}
		} catch (NullPointerException e2) {
			//fine, no recursionLevel was specified.
		}

		if (!element.hasChildNodes()) {
			//find the type of the element, if registered, or create a new one

			try {
				type = ((Element) element).getAttributeNode("type")
						.getNodeValue();
				try {
					//try to get a simpleType for this element
					Gene t = (Gene) simpleTypes.get(type);

					if (isRecChild) {
						recChild.addBackup(t);
						return (Gene) recChild;
					} else {
						if (omitTag) {
							elementGene =
								new ToxRecursiveMixedContent(min_qtty, max_qtty, myRandom,
																						 true, resetElement,
																						 tgEngine.addNewLines());
						} else {
							elementGene = new ToxElement(name, min_qtty, max_qtty, myRandom,
																					 true, resetElement, isRecAncestor,
																					 tgEngine);
						}
						elementGene.addContent(t);
					}
				} catch (KeyNotFoundException e1) {
					try {
						//it can be a complexType then
						ToxComplexType complex = (ToxComplexType) complexTypes
								.get(type);

						//create the gene for the element
						if (omitTag) {
							elementGene =
								new ToxRecursiveMixedContent(min_qtty, max_qtty, myRandom,
																						 false,resetElement,
																						 tgEngine.addNewLines());
						} else {
							elementGene = new ToxElement(name, min_qtty, max_qtty, myRandom,
																					 false, resetElement, isRecAncestor,
																					 tgEngine);
						}
						elementGene.addComplexType(complex);
					} catch (KeyNotFoundException e2) {
						try {
							SimpleTypeFactory factory=
								(SimpleTypeFactory) simpleTypeFactories.get(type);

							//create the gene for the element
							if (omitTag) {
								elementGene =
									new ToxRecursiveMixedContent(min_qtty, max_qtty, myRandom,
																							 true,resetElement,
																							 tgEngine.addNewLines());
							} else {
								elementGene = new ToxElement(name, min_qtty, max_qtty,myRandom,
																						 true, resetElement,isRecAncestor,
																						 tgEngine);
							}
							elementGene.addContent(factory.createGene());
						} catch (KeyNotFoundException e3) {
							//now we must give up
							throw new ToXgeneErrorException("Cannot find type \"" + type + "\"",
									findElement(element));
						}
					}
				}
			} catch (NullPointerException e3) {
				if (isRecChild) {
					return (Gene) recChild;
				}
				throw new ToXgeneErrorException("no type specified for element \""
																				+ name + "\"", findElement(element));
			}

			return (Gene) elementGene;
		} else {
			//the element type is declared by its children. If a "type"
			// attribute is
			//provied, it is overriden.

			Node node = element.getFirstChild();
			String elmType = node.getNodeName();

			if (elmType.compareTo("simpleType") == 0) {
				//create the gene for the element
				if (omitTag) {
					elementGene =
						new ToxRecursiveMixedContent(min_qtty, max_qtty, myRandom, true,
																				 resetElement, tgEngine.addNewLines());
				} else {
					elementGene = new ToxElement(name, min_qtty, max_qtty,myRandom, true,
																			 resetElement, isRecAncestor, tgEngine);
				}
				tgEngine.getToXgeneReporter().explain("Processing anonymous SimpleType in: " + name);

				//we can call createSimpleTypeGene here
				Node restriction = node.getFirstChild();

				if ((((Element) restriction).getElementsByTagName("tox-expr"))
						.getLength() != 0) {
					//this simple type has a scan/sample descendant
					Node scanNode = restriction.getFirstChild();
					String nodeName = scanNode.getNodeName();
					if ((nodeName.compareTo("tox-scan") != 0)
							&& (nodeName.compareTo("tox-sample") != 0)
							&& (nodeName.compareTo("tox-foreach") != 0)) {
						throw new ToXgeneErrorException("invalid simpleType declaration",
								findElement(restriction));
					}
					ToxScan myScan = createToxScanGene(scanNode, scan, true,
							null, min_qtty, max_qtty);
					elementGene.addScan(myScan);
				} else {
					elementGene.addContent(createSimpleTypeGene(restriction));
				}

				tgEngine.getToXgeneReporter().explain("-----> done SimpleType in: " + name);

				return (Gene) elementGene;
			}

			if (elmType.compareTo("complexType") == 0) {
				//create the gene for the element
				if (omitTag) {
					elementGene =
						new ToxRecursiveMixedContent(min_qtty, max_qtty, myRandom, false,
																				 resetElement, tgEngine.addNewLines());
				} else {
					elementGene = new ToxElement(name, min_qtty, max_qtty,myRandom,false,
																			 resetElement, isRecAncestor,tgEngine);
				}

				//here we create a new ToxComplexType
				tgEngine.getToXgeneReporter().explain("Processing anonymous ComplexType in: " + name);

				String mixed = ((Element) node).getAttributeNode("mixed")
						.getNodeValue();

				if (mixed.compareTo("true") == 0) {
					elementGene.addComplexType(createComplexType(node
							.getChildNodes(), scan, myRecursive, true,
							min_qtty, max_qtty));
				} else {
					elementGene.addComplexType(createComplexType(node
							.getChildNodes(), scan, myRecursive, false,
							min_qtty, max_qtty));
				}

				tgEngine.getToXgeneReporter().explain("-----> done ComplexType in: " + name);

				if (isRecAncestor) {
					try {
						for (int i = 0; i < recChildren.size(); i++) {
							ToxRecursiveElement child = (ToxRecursiveElement) recChildren
									.get(i);
							child.setContent(elementGene, recLevels,
									recChildren);
						}
					} catch (NullPointerException e) {
						tgEngine.getToXgeneReporter().warning("no recursive descendant for element: "
								+ name + " was found.");
					}
					recChildren = null;
				}

				return (Gene) elementGene;
			}

			if (elmType.compareTo("tox-expr") == 0) {
				ToxGet get = createToxGetGene(node, scan, min_qtty, max_qtty);
				if (isRecChild) {
					recChild.addBackup(get);
					return (Gene) recChild;
				}

				if (omitTag) {
					elementGene =
						new ToxRecursiveMixedContent(min_qtty, max_qtty, myRandom, true,
																				 resetElement, tgEngine.addNewLines());
				} else {
					elementGene = new ToxElement(name, min_qtty, max_qtty,myRandom, true,
																			 resetElement, isRecAncestor, tgEngine);
				}
				elementGene.addVarQttyContent(get);

				return (Gene) elementGene;
			}
		}
		throw new ToXgeneErrorException("invalid element definition:" + name,
																		findElement(element));
	}

	/**
	 * Creates a gene for an attribute declaration
	 */
	private Gene createToxAttributeGene(Node node, ToxScan scan) {
		Gene gene = null;
		ToxAttribute attribute = null;
		String name, distribution = "", type = "", int_scan;
		char separator = 0;
		ToxRandom myRandom = null;
		int min_qtty = 0, max_qtty = 0;
		boolean get_max_qtty = false;

		//attribute nodes have name, tox-minqtty and tox-maxqtty as required
		//attributes. Optionally, they have a tox-distribution and a type
		//declaration
		name = ((Element) node).getAttributeNode("name").getNodeValue();
		tgEngine.getToXgeneReporter().explain("Processing attribute: " + name);

		try {
			distribution = ((Element) node)
					.getAttributeNode("tox-distribution").getNodeValue();
			myRandom = (ToxRandom) randomGenerators.get(distribution);

			min_qtty = (int) myRandom.minValue();
			max_qtty = (int) myRandom.maxValue();
		} catch (KeyNotFoundException e1) {
			throw new ToXgeneErrorException("Invalid distribution name: "
																			+ distribution,findElement(node));
		} catch (NullPointerException e2) {
			int_scan = ((Element) node).getAttributeNode("tox-minOccurs")
					.getNodeValue();
			min_qtty = Integer.parseInt(int_scan);

			int_scan = ((Element) node).getAttributeNode("tox-maxOccurs")
					.getNodeValue();
			if (int_scan.compareTo("unbounded") != 0) {
				max_qtty = Integer.parseInt(int_scan);
				myRandom = tgEngine.newUniformDistribution(min_qtty, max_qtty);
			} else {
				max_qtty = -1;
			}
		}

		try {
			String temp = ((Element) node).getAttributeNode("tox-distribution")
					.getNodeValue();
			if (temp.length() != 1) {
				throw new ToXgeneErrorException("Invalid separator \"" + temp
						+ "\" for multivalued " + "attribute!");
			}
			separator = temp.charAt(0);
		} catch (NullPointerException e) {
			//do nothing, no separator was specified.
		}

		if (node.hasChildNodes()) {
			//the gene of this attribute is specified by its children, here we
			// can
			//have one of  or 

			Node child = node.getFirstChild();

			if (child.getNodeName().compareTo("tox-expr") == 0) {
				gene = createToxGetGene(child, scan, min_qtty, max_qtty);
				get_max_qtty = true;
			} else {
				if (child.getNodeName().compareTo("simpleType") == 0) {

					Node restriction = child.getFirstChild();

					if ((((Element) restriction)
							.getElementsByTagName("tox-expr")).getLength() != 0) {
						//this simple type has a scan/sample descendant
						Node scanNode = restriction.getFirstChild();
						String nodeName = scanNode.getNodeName();
						if ((nodeName.compareTo("tox-scan") != 0)
								&& (nodeName.compareTo("tox-sample") != 0)
								&& (nodeName.compareTo("tox-foreach") != 0)) {
							throw new ToXgeneErrorException("invalid simpleType declaration",
														 findElement(restriction));
						}
						ToxScan myScan = createToxScanGene(scanNode, scan,
								true, null, min_qtty, max_qtty);
						gene = myScan;
						get_max_qtty = true;
					} else {
						gene = createSimpleTypeGene(restriction);
					}
				}
			}
		} else {
			//this attribute element has to have its gene specified by the type
			//attribute
			try {
				type = ((Element) node).getAttributeNode("type").getNodeValue();
				gene = (Gene) simpleTypes.get(type);
			} catch (KeyNotFoundException e1) {
				//the type provided is not a user-defined simpleType, it could
				// be a
				//base type, for which a standard gene is used
				try {
					SimpleTypeFactory factory = (SimpleTypeFactory) simpleTypeFactories
							.get(type);
					gene = factory.createGene(node.getChildNodes());
				} catch (KeyNotFoundException e2) {
					//this is an invalid type declaration!
					throw new ToXgeneErrorException("invalid \"" + name
												 + "\" attribute declaration: " + e2.getMessage(),
												 findElement(node));
				}
			} catch (NullPointerException e1) {
				//no type information provided, this attribute is either
				// anonymous or
				//has a tox-expr as its contents.
				throw new ToXgeneErrorException("invalid \"" + name
											 + "\" attribute declaration: no type" + " specified",
											 findElement(node));
			}
		}

		attribute = new ToxAttribute(name, min_qtty, max_qtty, gene, myRandom,
				get_max_qtty, separator);

		tgEngine.getToXgeneReporter().explain("-----> done attribute: " + name);

		return attribute;
	}

	/**
	 * Creates a gene for a tox-expr element
	 */
	private ToxGet createToxGetGene(Node node, ToxScan scan, int min, int max) {
		ToxGet gene = null;
		String query = "", format = "";

		try {
			query = ((Element) node).getAttributeNode("value").getNodeValue();
		} catch (NullPointerException e) {
			throw new ToXgeneErrorException("no query specified for tox-expr item!",
										 findElement(node));
		}

		try {
			format = ((Element) node).getAttributeNode("format").getNodeValue();
		} catch (NullPointerException e) {
		}

		tgEngine.getToXgeneReporter().explain("Processing tox-expr for: " + query);
		gene = new ToxGet(ExpressionParser.parse(query, scan, findElement(node),
																						 tgEngine.getToXgeneReporter(),
																						 tgEngine.simpleTypes()),
											min, max, format, tgEngine);
		return gene;
	}

	/**
	 * Creates a gene for a tox-alternatives element
	 */
	private ToxAlternatives createToxAlternativesGene(Node node, ToxScan scan,
			String recursive, boolean mixed, int min_qtty, int max_qtty) {
		ToxAlternatives gene;
		ToxComplexType complex = null;
		ToxOption option = null;
		ToxRandom myRandom = null;
		NodeList children = node.getChildNodes();
		Node child;

		tgEngine.getToXgeneReporter().explain("Processing tox-alternatives:");

		try {
			String distribution = ((Element) node).getAttributeNode(
					"tox-distribution").getNodeValue();
			myRandom = (ToxRandom) randomGenerators.get(distribution);

			int min = (int) myRandom.minValue();
			int max = (int) myRandom.maxValue();

			if (min < 0 || max > 100) {
				throw new ToXgeneErrorException("the probability distribution for "
											 + "choosing among alternatives has to be in [0,100]",
											 findElement(node));
			}
			if (min != 0 || max != 100) {
				tgEngine.getToXgeneReporter().warning("probabiliy distribution != [0,100]!\n"
						+ "\tno action taken");
			}
		} catch (KeyNotFoundException e1) {
			throw new ToXgeneErrorException("invalid distribution name: ",
																			findElement(node));
		} catch (NullPointerException e2) {
			myRandom = tgEngine.newUniformDistribution(0, 100);
		}

		gene = new ToxAlternatives(myRandom);

		for (int i = 0; i < children.getLength(); i++) {
			child = children.item(i);
			if (child.getNodeName().compareTo("tox-option") == 0) {

				//retrieves the probability of occurrence for this option
				Float percent = new Float(((Element) child).getAttributeNode(
						"odds").getNodeValue());

				tgEngine.getToXgeneReporter().explain("Processing tox-option; odds = " + percent);

				//create the gene for this option
				option = new ToxOption();
				complex = createComplexType(child.getChildNodes(), scan,
						recursive, mixed, min_qtty, max_qtty);
				Vector temp1 = complex.contents();
				for (int j = 0; j < temp1.size(); j++) {
					option.addElement((Gene) temp1.elementAt(j));
				}
				Vector temp2 = complex.attributes();
				for (int j = 0; j < temp2.size(); j++) {
					option.addAttribute((Gene) temp2.elementAt(j));
				}

				gene.addOption(percent.floatValue(), option);

				tgEngine.getToXgeneReporter().explain("-----> done tox-option");
			}
		}

		tgEngine.getToXgeneReporter().explain("-----> done tox-alternatives");

		return gene;
	}

	/**
	 * Creates a gene for a tox-alternatives element
	 */
	private ToxIf createToxIfGene(Node node, ToxScan scan, String recursive,
			boolean mixed, int min_qtty, int max_qtty) {
		ToxIf gene;
		ToxComplexType complex = null;
		ToxOption thenBlock = null, elseBlock = null;
		NodeList children = node.getChildNodes();
		Node child;

		tgEngine.getToXgeneReporter().explain("Processing tox-if:");

		if (scan == null) {
			throw new ToXgeneErrorException(
					"tox-if declared out of tox-scan/tox-sample/tox-foreach "
							+ "scope!", findElement(node));
		}

		String exp = ((Element) node).getAttributeNode("expr").getNodeValue();

		//the first child of a tox-if is a tox-then element
		child = children.item(0);
		tgEngine.getToXgeneReporter().explain("Processing tox-then clause");

		//create the gene for this option
		thenBlock = new ToxOption();
		complex = createComplexType(child.getChildNodes(), scan, recursive,
				mixed, min_qtty, max_qtty);
		Vector temp1 = complex.contents();
		for (int j = 0; j < temp1.size(); j++) {
			thenBlock.addElement((Gene) temp1.elementAt(j));
		}
		temp1 = complex.attributes();
		for (int j = 0; j < temp1.size(); j++) {
			thenBlock.addAttribute((Gene) temp1.elementAt(j));
		}

		tgEngine.getToXgeneReporter().explain("-----> done tox-then");

		gene = new ToxIf(new WhereClause(exp, scan, findElement(node),
																		 tgEngine.getToXgeneReporter(),
																		 tgEngine.simpleTypes()),
										 thenBlock);

		//the else child is optional
		if (children.getLength() == 2) {
			child = children.item(1);
			tgEngine.getToXgeneReporter().explain("Processing tox-else clause");

			//create the gene for this option
			elseBlock = new ToxOption();
			complex = createComplexType(child.getChildNodes(), scan, recursive,
					mixed, min_qtty, max_qtty);
			temp1 = complex.contents();
			for (int j = 0; j < temp1.size(); j++) {
				elseBlock.addElement((Gene) temp1.elementAt(j));
			}
			temp1 = complex.attributes();
			for (int j = 0; j < temp1.size(); j++) {
				elseBlock.addAttribute((Gene) temp1.elementAt(j));
			}

			tgEngine.getToXgeneReporter().explain("-----> done tox-else");
			gene.addElseBlock(elseBlock);
		}
		tgEngine.getToXgeneReporter().explain("-----> done tox-if");
		return gene;
	}

	private ToxScan createToxScanGene(Node node, ToxScan master,
			boolean hasMixedContent, String recursive, int min_qtty,
			int max_qtty) {
		ToxScan myScan;
		boolean isSample = (node.getNodeName().compareTo("tox-sample") == 0);
		boolean isForeach = (node.getNodeName().compareTo("tox-foreach") == 0);
		ToxRandom myRandom = null;
		String path = "", duplicates = "", varName = "", where = "";

		path = ((Element) node).getAttributeNode("path").getNodeValue();

		if (isSample) {
			tgEngine.getToXgeneReporter().explain("Processing tox-sample: ");
		} else {
			if (isForeach) {
				tgEngine.getToXgeneReporter().explain("Processing tox-foreach: ");
			} else {
				tgEngine.getToXgeneReporter().explain("Processing tox-scan: ");
			}
		}

		try {
			varName = ((Element) node).getAttributeNode("name").getNodeValue();
		} catch (NullPointerException e) {
		}

		try {
			where = ((Element) node).getAttributeNode("where").getNodeValue();
		} catch (NullPointerException e) {
		}

		if (isSample) {
			duplicates = ((Element) node).getAttributeNode("duplicates")
					.getNodeValue();

			//get a user defined distribution
			try {
				String distribution = ((Element) node).getAttributeNode(
						"tox-distribution").getNodeValue();
				myRandom = (ToxRandom) randomGenerators.get(distribution);
				if (myRandom.minValue() != 0) {
					tgEngine.getToXgeneReporter().warning("minimum value in distribution "
													 + distribution + " != 0.\n Some elements in "
													 + path + " might never be accessed!");
				}
			} catch (NullPointerException e) {
			}
		}

		if (isSample) {
			tgEngine.getToXgeneReporter().explain("path: " + path + " duplicates: " + duplicates);
		} else {
			tgEngine.getToXgeneReporter().explain("path: " + path);
		}

		myScan = new ToxScan(path, master, varName, isForeach, isSample,
												 duplicates.compareTo("yes") == 0, myRandom, where,
												 findElement(node), tgEngine);

		//process all the children of this element
		NodeList children = node.getChildNodes();
		ToxComplexType myHolder = createComplexType(children, myScan,
				recursive, hasMixedContent, min_qtty, max_qtty);
		myScan.addComplexType(myHolder);

		return myScan;
	}

};




© 2015 - 2024 Weber Informatics LLC | Privacy Policy