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

org.xdef.util.xd2xsd.Xd2Xsd Maven / Gradle / Ivy

There is a newer version: 42.2.13
Show newest version
package org.xdef.util.xd2xsd;

import java.util.HashMap;
import java.util.Map;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xdef.XDContainer;
import org.xdef.XDNamedValue;
import org.xdef.XDParser;
import org.xdef.XDPool;
import org.xdef.XDValue;
import org.xdef.impl.XData;
import org.xdef.impl.XVariable;
import org.xdef.impl.parsers.XSParseList;
import org.xdef.impl.parsers.XSParseUnion;
import org.xdef.model.XMData;
import org.xdef.model.XMDefinition;
import org.xdef.model.XMElement;
import org.xdef.model.XMNode;
import static org.xdef.model.XMNode.XMTEXT;
import org.xdef.model.XMOccurrence;
import org.xdef.model.XMSelector;
import org.xdef.model.XMVariable;
import org.xdef.msg.XDCONV;
import org.xdef.sys.SRuntimeException;
import org.xdef.xml.KXmlUtils;

/** Convertor of X-definition to XML Schema.
 * @author Vaclav Trojan
 */
public class Xd2Xsd {
	/** Prefix used for the XML schema namespace. */
	private static final String SCHEMA_PFX = "xs:";
	/** Target namespace of XML schema with declared user types. */
	private static final String USERTYPES_URI = "$_types_$";
	/** QName of schema element. */
	private static final QName SCHEMA_QNAME =
		new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "schema");
	/** Name of root XML schema file. */
	private final String _rootName;
	/** Name of XML schema file with user declared types. */
	private final String _typesName;
	/** Map of file names and XML schema elements.*/
	private final Map _xsdSources;
	/** Switch if generate annotation with documentation information. */
	private final boolean _genInfo;
	/**  Switch generate xdatatime outFormat. */
	private final boolean _genXdateOutFormat;
	/** org.w3c.dom.Document used for creation of nodes. */
	private final Document _doc;
	/** XML schema element with of user declared types.*/
	private final Element _types;

	/** Create new instance of XsdGenerator.
	 * @param xp XDPool with X-definitions.
	 * @param outType name of XML schema file with type declarations. May be
	 * @param genInfo if true the annotations with documentation is generated.
	 * @param genXdateOutFormat if true, from the xdatetime method the outFormat
	 * parameter (the second sequential) is used as mask to validate datetime.
	 */
	private Xd2Xsd(final XDPool xp,
		final String outType,
		final boolean genInfo,
		final boolean genXdateOutFormat) {
		_rootName = outType;
		_genInfo = genInfo;
		_genXdateOutFormat = genXdateOutFormat;
		_xsdSources = new HashMap<>();
		_doc = KXmlUtils.newDocument();
		if (null != outType && !outType.isEmpty()) {
			Element types = genDeclaredTypes(xp);
			if (types != null && types.getChildNodes().getLength() > 0) {
				_types = types;
				_typesName = outType;
				addSchema(_typesName, _types);
			} else {
				_types = null;
				_typesName = null;
			}
		} else {
			_typesName = null;
			_types = null;
		}
	}

	private Element genDeclaredTypes(final XDPool xp) {
		XMVariable[] vars = xp.getVariableTable().toArray();
		Element types = genNewSchema();
		types.setAttribute("targetNamespace", USERTYPES_URI);
		types.setAttribute("xmlns", USERTYPES_URI);
		for (XMVariable v : vars) {
			if (v.getName().charAt(0) != '$') {
				XData xdata = new XData("$text", null, xp, XMTEXT);
				xdata._check = ((XVariable)v).getParseMethodAddr();
				GenParser parserInfo =
					GenParser.genParser( (XMData) xdata, _genXdateOutFormat);
				String typeName = genDeclaredName(parserInfo);
				String vName =  v.getName().replace('#', '_');
				if (typeName != null && !typeName.isEmpty()
					&& !vName.equals(typeName)) {
					if (findSchematype(types, v.getName()) == null) {
						Element simpleType = genSchemaElem(types, "simpleType");
						simpleType.setAttribute("name", vName);
						Element restr = genRestrictionElement(simpleType);
						restr.setAttribute("base", typeName);
					}
				}
				if (typeName != null && !typeName.isEmpty()) {
					if (findSchematype(types, typeName) != null) {
						continue;
					}
					Element simpleType = genSchemaElem(types, "simpleType");
					simpleType.setAttribute("name", typeName);
					if (parserInfo.getParser().getNamedParams().isEmpty()) {
						addDocumentation(simpleType, parserInfo.getInfo());
						Element restr = genRestrictionElement(simpleType);
						String parserName =
							SCHEMA_PFX + parserInfo.getParser().parserName();
						restr.setAttribute("base", parserName);
					} else {
						genRestrictions(simpleType, parserInfo);
					}
				}
			}
		}
		return types.getChildNodes().getLength() > 0 ? types : null;
	}

	/** Get genXdateOutFormat swithch.
	 * @return genXdateOutFormat swithch.
	 */
	protected final boolean isGenXdateOutFormat() { return _genXdateOutFormat;}

	/** If genInfo switch is on, then Generate annotations with documentation.
	 * @param parent node where to add annotation.
	 * @param text of documentation.
	 */
	private void addDocumentation(final Element parent, final String text) {
		if (_genInfo && text != null && !text.trim().isEmpty()) {
			NodeList nl = KXmlUtils.getChildElementsNS(parent,
				XMLConstants.W3C_XML_SCHEMA_NS_URI, "annotation");
			Element anotation =  (nl != null && nl.getLength() > 0)
				? (Element) nl.item(0) : genSchemaElem(parent, "annotation");

			Element documentation = genSchemaElem(anotation, "documentation");
			documentation.appendChild(_doc.createTextNode(text.trim()));
		}
	}

	/** Get name of XML schema type.
	 * @param parserInfo parser information.
	 * @return schema type name.
	 */
	private static String getSchemaTypeName(final GenParser parserInfo) {
		return SCHEMA_PFX + parserInfo.getParser().parserName();
	}

	/** Create element with restrictions,
	 * @param parent element where to add restrictions
	 * @param parserInfo object containing restriction information.
	 * @return element with restrictions.
	 */
	private Element genRestrictions(final Element parent,
		final GenParser parserInfo) {
		addDocumentation(parent, parserInfo.getInfo());
		String typeName = getSchemaTypeName(parserInfo);
		switch(typeName) {
			case "xs:union": {
				XDParser[] parsers =
					((XSParseUnion) parserInfo.getParser()).getParsers();
				Element union = genSchemaElem(parent, "union");
				for (XDValue xval: parsers) {
					XDParser p = (XDParser) xval;
					Element simpletp = genSchemaElem(union, "simpleType");
					genRestrictions(simpletp,
						SCHEMA_PFX+p.parserName(),
						p.getNamedParams().getXDNamedItems());
				}
				return union;
			}
			case "xs:list": {
				Element list = genSchemaElem(parent, "list");
				XDParser p =
					((XSParseList) parserInfo.getParser()).getItemParser();
				Element simpletp = genSchemaElem(list, "simpleType");
				genRestrictions(simpletp,
					SCHEMA_PFX+p.parserName(),
					p.getNamedParams().getXDNamedItems());
				return list;
			}
		}
		XDNamedValue[] xdv =
			parserInfo.getParser().getNamedParams().getXDNamedItems();
		return genRestrictions(parent, typeName, xdv);
	}

	/** Create element with restrictions,
	 * @param parent element where to add restrictions
	 * @param typeName name of type.
	 * @param namedParams named parameters.
	 * @return element with restrictions.
	 */
	private Element genRestrictions(final Element parent,
		final String typeName,
		final XDNamedValue[] namedParams) {
		Element restr = genSchemaElem(parent, "restriction");
		restr.setAttribute("base", typeName);
		for (XDNamedValue x: namedParams) {
			XDValue xval = x.getValue();
			if (xval==null) {
				continue;
			}
			String paramName = x.getName();
			Element param;
			switch(paramName) {
				case "pattern":
				case "enumeration":
					if (xval instanceof XDContainer) {
						XDContainer xdc = (XDContainer) xval;
						for (int i = 0; i < xdc.getXDItemsNumber(); i++) {
							param = genSchemaElem(restr, paramName);
							param.setAttribute("value",
								xdc.getXDItem(i).toString());
						}
					} else {
						param = genSchemaElem(restr, paramName);
						param.setAttribute("value", xval.toString());
					}
					continue;
				case "minLength":
				case "maxLength":
				case "length":
				case "minInclusive":
				case "minExclusive":
				case "maxInclusive":
				case "maxExclusive":
				case "totalDigits":
				case "fractionDigits":
					param = genSchemaElem(restr, paramName);
					param.setAttribute("value", xval.toString());
			}
		}
		return restr;
	}

	/** Create xd:sequence element as child of argument.
	 * @param el parent of created element.
	 * @return xd:sequence element as child of argument.
	 */
	private Element genSequenceElement(final Element el) {
		return XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(el.getNamespaceURI())
		 && "sequence".equals(el.getLocalName())
			? el : genSchemaElem(el, "sequence");
	}

	/** Create xd:restriction element as child of argument.
	 * @param el parent of created element.
	 * @return xd:restriction element as child of argument.
	 */
	private Element genRestrictionElement(final Element el) {
		return XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(el.getNamespaceURI())
		 && "restriction".equals(el.getLocalName())
			? el : genSchemaElem(el, "restriction");
	}

	/** Generate group of models.
	 * @param parent element where to add models.
	 * @param xsel group selector.
	 * @param children array with children.
	 * @param endIndex index of the last child.
	 * @param index index of the first child.
	 * @return the index where to continue.
	 */
	private int genGroup(final Element complt,
		final Element parent,
		final XMSelector xsel,
		final XMNode[] children,
		final int index) {
		int endIndex = xsel.getEndIndex();
		Element sel;
		switch (xsel.getKind()) {
			case XMNode.XMSEQUENCE:
				sel = genSequenceElement(parent);
				setOccurrence(sel, xsel);
				return genSequence(complt, sel, children, index, endIndex);
			case XMNode.XMCHOICE:
				sel = genSchemaElem(parent, "choice");
				setOccurrence(sel, xsel);
				return genSequence(complt, sel, children, index, endIndex);
			case XMNode.XMMIXED: {
				int min = 0;
				int max = 0;
				for (int i = index + 1; i < endIndex; i++) {
					XMNode x = children[i];
					switch (x.getKind()) {
						case XMNode.XMMIXED:
						case XMNode.XMCHOICE:
						case XMNode.XMSEQUENCE:
						case XMNode.XMELEMENT:
							XMOccurrence occ = x.getOccurence();
							if (occ.minOccurs() > 0) {
								min++;
							}
							if (occ.maxOccurs() >= 1) {
								max = occ.maxOccurs() == Integer.MAX_VALUE
									|| max==Integer.MAX_VALUE
									? Integer.MAX_VALUE : max + occ.maxOccurs();
							}
					}
				}
				sel = genSequenceElement(parent);
				sel = genSchemaElem(sel, "choice");
				sel.setAttribute("maxOccurs", max == Integer.MAX_VALUE
					? "unbounded" : String.valueOf(max));
				sel.setAttribute("minOccurs", String.valueOf(min));
				return genSequence(complt, sel, children, index, endIndex);
			}
			default:
				//"&{0}" not implemented
				throw new SRuntimeException(XDCONV.XDCONV205, xsel);
		}
	}

	/** Generate sequence of models.
	 * @param complt complexType element.
	 * @param parent node where to add models.
	 * @param children array with children.
	 * @param index index of the first child.
	 * @param endIndex index of the last child.
	 * @return the index where to continue.
	 */
	private int genSequence(final Element complt,
		final Element parent,
		final XMNode[] children,
		final int index,
		final int endIndex) {
		int i = index + 1;
		for (; i < endIndex; i++) {
			XMNode x = children[i];
			switch (x.getKind()) {
				case XMNode.XMELEMENT:
					genElem(parent, (XMElement) x);
					continue;
				case XMNode.XMTEXT:
					complt.setAttribute("mixed", "true");
					continue;
				case XMNode.XMMIXED:
				case XMNode.XMCHOICE:
				case XMNode.XMSEQUENCE: {
					XMSelector xsel = (XMSelector) x;
					i = genGroup(complt, parent, xsel, children, i);
					continue;
				}
				case XMNode.XMSELECTOR_END:
					return i + 1; // sholdn't happen!
			}
		}
		return i;
	}

	/** Set occurrence restrictions to an element.
	 * @param el Element where to set restrictions.
	 * @param xel X-definition model of element.
	 */
	private static void setOccurrence(final Element el, final XMNode xel) {
		XMOccurrence occ = xel.getOccurence();
		int minOcc = occ.minOccurs();
		int maxOcc = occ.maxOccurs();
		if (minOcc != 1 || maxOcc != 1) {
			el.setAttribute("minOccurs", String.valueOf(minOcc));
			el.setAttribute("maxOccurs", Integer.MAX_VALUE==maxOcc
				? "unbounded":String.valueOf(maxOcc));
		}
	}

	/** Get declared type name.
	 * @param parserInfo object with information about value type.
	 * @return declared type name or null;
	 */
	private static String genDeclaredName(final GenParser parserInfo) {
		String s = parserInfo.getDeclaredName();
		if (s != null) {
			String[] parts = s.split(";");
			if (parts.length>0&&!(s=parts[0].trim()).isEmpty()) {
				int ndx = s.indexOf('#');
				return ndx>=0 ? s.substring(0,ndx)+"_"+s.substring(ndx+1) : s;
			}
		}
		return null;
	}

	/** Add attributes to schema element.
	 * @param el schema element
	 * @param attrs array with attribute models.
	 */
	private void addAttrs(final Element el, final XMData[] attrs) {
		for (XMNode x: attrs) {
			Element att = genSchemaElem(el, "attribute");
			String targetNs =
				getSchemaRoot(el).getAttribute("targetNamespace");
			XMOccurrence attOcc = x.getOccurence();
			att.setAttribute("use",	attOcc.isRequired()?"required":"optional");
			String nsUri = x.getNSUri();
			if (nsUri != null && !nsUri.isEmpty()) {
				if (!targetNs.equals(nsUri)) {
					att.setAttribute("ref", x.getLocalName());
					att.setAttribute("xmlns", nsUri);
					String outName = findSchemaItem(nsUri);
					Element schemaItem;
					if (outName == null) {
						outName = genNewName();
						schemaItem = genNewSchema(outName);
						schemaItem.setAttribute("targetNamespace", nsUri);
					} else {
						schemaItem = _xsdSources.get(outName);
					}
					att = genSchemaElem(schemaItem, "attribute");
				} else {
					att.setAttribute("form", "qualified");
				}
			}
			att.setAttribute("name", x.getLocalName());
			GenParser parserInfo =
				GenParser.genParser((XMData) x, _genXdateOutFormat);
			if (parserInfo.getFixed() != null) {
				att.setAttribute("fixed", parserInfo.getFixed());
			} else if (parserInfo.getDefault() != null) {
				att.setAttribute("default", parserInfo.getDefault());
			}
			String typeName = genDeclaredName(parserInfo);
			if (parserInfo.getParser().getNamedParams().isEmpty()) {
				String parserName =
					SCHEMA_PFX + parserInfo.getParser().parserName();
				if (typeName == null) {
					att.setAttribute("type",parserName);
				} else {
					Element tpel = _types == null ? getSchemaRoot(el) : _types;
					Element simpletp = findSchematype(tpel, typeName);
					if (simpletp == null) {
						simpletp = genSchemaElem(tpel, "simpleType");
						addDocumentation(simpletp, parserInfo.getInfo());
						simpletp.setAttribute("name", typeName);
						Element restr = genRestrictionElement(simpletp);
						restr.setAttribute("base", parserName);
					}
					att.setAttribute("type", typeName);
				}
			} else {
				if (typeName == null) {
					Element simpletp = genSchemaElem(att, "simpleType");
					genRestrictions(simpletp, parserInfo);
				} else {
					att.setAttribute("type", typeName);
					Element tpel = _types == null ? getSchemaRoot(el) : _types;
					Element simpleType = findSchematype(tpel, typeName);
					if (simpleType == null) {
						simpleType = genSchemaElem(tpel, "simpleType");
						genRestrictions(simpleType, parserInfo);
						simpleType.setAttribute("name", typeName);
					}
				}
			}
		}
	}

	/** Find simpleType element with given name.
	 * @param el element where to find.
	 * @param name name of simpleType (may be null).
	 * @return simpleType element with given name or null.
	 */
	private Element findSchematype(final Element el, final String name) {
		if (name != null) {
			Element root = getSchemaRoot(el);
			NodeList nl = KXmlUtils.getChildElementsNS(
				root, XMLConstants.W3C_XML_SCHEMA_NS_URI, "simpleType");
			for (int i=0; i < nl.getLength(); i++) {
				Element stype = (Element)nl.item(i);
				if (stype.getAttribute("name").equals(name)) {
					return stype;
				}
			}
		}
		return null;
	}

	/** Get unique schemaType name in this schema element.
	 * @param el element where to the name must be unique.
	 * @param name the name to search.
	 * @return unique type name.
	 */
	private String createSchemaTypeName(final Element el, final String name) {
		if (name == null) {
			return null;
		}
		String typeName = name;
		if (findSchematype(el, name) == null) {
			return typeName;
		}
		int i = 1;
		String s;
		while (findSchematype(el, s = typeName + "_" + i) != null) {
			i++;
		}
		return s;
	}

	/** Get root schema element.
	 * @param el actual element,
	 * @return root schema element
	 */
	private Element getSchemaRoot(final Element el) {
		Element schema = el;
		for (;;) {
			if ((SCHEMA_PFX+"schema").equals(schema.getNodeName())) {
				return schema;
			}
			Node node = schema.getParentNode();
			if (node.getNodeType() == Node.ELEMENT_NODE) {
				schema = (Element) node;
			} else {
				//XML shema not found
				throw new SRuntimeException(XDCONV.XDCONV206);
			}
		}
	}

	/** If data value is optional, generate xs:complexType, xs:simpleContent
	 * and xs:extension.
	 * @param elem element declaration.
	 * @param xData data value model.
	 * @param parserInfo created object.
	 * @return If data value is optional, generate union with given type
	 * and string with pattern xs:simpleContent and xs:extension with attribute
	 * "base". Return the element xs:extension or return null;
	 */
	private Element genOptionalTextType(final Element elem,
		final XMData xData,
		final GenParser parserInfo) {
		if (!xData.getOccurence().isOptional()
			|| parserInfo.getFixed()!=null || parserInfo.getDefault()!=null) {
			return null;
		}
		Element el1 = genSchemaElem(elem, "complexType");
		Element el2 = genSchemaElem(el1, "simpleContent");
		XDParser p = parserInfo.getParser();
		String info = "In the X-definition is declared optional text item";
		if (p.getDeclaredName() != null) {
			info += " (type: '" + p.getDeclaredName() + "')";
		}
		info += " as " + p.parserName();
		info += GenParser.displayParams(p.getNamedParams());
		addDocumentation(el2, info);
		String typeName = genDeclaredName(parserInfo);
		String typeName1 = "_" + elem.getAttribute("name").replace(':', '_');
		Element tpel = _types == null ? getSchemaRoot(elem) : _types;
		Element simpleType;
		if (typeName != null) {
			if (findSchematype(tpel, typeName) == null) {
				simpleType = genSchemaElem(tpel, "simpleType");
				simpleType.setAttribute("name", typeName);
				genRestrictions(simpleType, parserInfo);
				typeName1 = typeName + typeName1;
			}
		}
		Element dummy = genSchemaElem(tpel, "simpleType");
		dummy.setAttribute("name", typeName1);
		Element union = genSchemaElem(dummy, "union");
		Element simpleType2;
		Element restriction;
		simpleType2 = genSchemaElem(union, "simpleType");
		if (typeName != null) {
			restriction = genSchemaElem(simpleType2, "restriction");
			restriction.setAttribute("base", typeName);
		} else {
			genRestrictions(simpleType2, parserInfo);
		}
		simpleType2 =  genSchemaElem(union, "simpleType");
		restriction = genSchemaElem(simpleType2, "restriction");
		restriction.setAttribute("base", "xs:string");
		Element pattern = genSchemaElem(restriction, "pattern");
		pattern.setAttribute("value", "\\s*");
		tpel.removeChild(dummy);
		int i = 0;
		String s = typeName1;
		Element e;
		while ((e = findSchematype(tpel, s)) != null) {
			if (KXmlUtils.compareElements(e, dummy).errors()) {
				s = typeName1 + "_" + (++i);
				dummy.setAttribute("name", s);
			} else {
				break;
			}
		}
		if (e == null) {
			tpel.appendChild(dummy);
		}
		el2 = genSchemaElem(el2, "extension") ;
		el2.setAttribute("base", s);
		return el2;
	}

	/** Create XML schema from X-definition model element.
	 * @param parent where add the model.
	 * @param xel X-definition model of element.
	 * @return created XML schema.
	 */
	private Element genElem(final Element parent, final XMElement xel) {
		Element schema = getSchemaRoot(parent);
		String targetNs = schema.getAttribute("targetNamespace");
		if (targetNs == null) {
			targetNs = "";
		}
		String nsUri = xel.getNSUri();
		if (nsUri != null && !nsUri.isEmpty()) {
			if (!nsUri.equals(targetNs)) {
				String outName = findSchemaItem(nsUri);
				Element schemaItem;
				if (outName == null) {
					outName = genNewName();
					schemaItem = genNewSchema(outName);
					schemaItem.setAttribute("targetNamespace", nsUri);
					Element imprt = genSchemaElem(null, "import");
					imprt.setAttribute("schemaLocation", outName + ".xsd");
					imprt.setAttribute("namespace", nsUri);
					schema.insertBefore(imprt, schema.getFirstChild());
				} else {
					schemaItem = _xsdSources.get(outName);
				}
				genElem(schemaItem, xel);
				Element elem = genSchemaElem(parent, "element");
				elem.setAttribute("xmlns", nsUri);
				elem.setAttribute("ref", xel.getLocalName());
				setOccurrence(elem, xel);
				return elem;
			}
		} else {
			if (!targetNs.isEmpty()) {
				String outName = findSchemaItem("");
				Element newSchema;
				if (outName == null) {
					outName = genNewName();
					newSchema = genNewSchema(outName);
					Element imprt = genSchemaElem(null, "import");
					imprt.setAttribute("schemaLocation", outName + ".xsd");
					schema.insertBefore(imprt, schema.getFirstChild());
				} else {
					newSchema = _xsdSources.get(outName);
				}
				genElem(newSchema, xel);
				Element elem = genSchemaElem(parent,"element");
				elem.setAttribute("ref", xel.getLocalName());
				setOccurrence(elem, xel);
				return elem;
			}
		}
		Element el = genSchemaElem(parent, "element");
		el.setAttribute("name", xel.getLocalName());
		if (!SCHEMA_QNAME.equals(new QName(parent.getNamespaceURI(),
			parent.getLocalName()))) { // skip if root element model
			setOccurrence(el, xel);
		}
		XMData[] attrs = xel.getAttrs();
		XMNode[] children = xel.getChildNodeModels();
		if (children.length == 1 && children[0].getKind() == XMNode.XMTEXT) {
			XMData xData = (XMData) children[0];
			GenParser parserInfo =
				GenParser.genParser(xData, _genXdateOutFormat);
			Element simpleType;
			if (attrs.length == 0) {
				Element extension = genOptionalTextType(el, xData, parserInfo);
				if (extension != null) {
					return el; // optional text item generated (no attributes)
				}
				if (parserInfo.getFixed() != null) {
					el.setAttribute("fixed", parserInfo.getFixed());
				} else if (parserInfo.getDefault() != null) {
					el.setAttribute("default", parserInfo.getDefault());
				}
				String typeName = genDeclaredName(parserInfo);
				if (typeName == null) {
					if (parserInfo.getParser().getNamedParams().isEmpty()) {
						el.setAttribute("type",
							SCHEMA_PFX + parserInfo.getParser().parserName());
					} else {
						simpleType = genSchemaElem(el, "simpleType");
						genRestrictions(simpleType, parserInfo);
					}
				} else {
					el.setAttribute("type", typeName);
					if (!targetNs.isEmpty()) {
						el.setAttribute("xmlns", targetNs);
					}
					Element tpel = _types == null ? schema : _types;
					simpleType = findSchematype(tpel, typeName);
					if (simpleType == null) {
						// named simpletype not exists, create it!
						simpleType = genSchemaElem(tpel,"simpleType");
						simpleType.setAttribute("name", typeName);
						genRestrictions(simpleType, parserInfo);
					}
				}
			} else { // attributes are there
				Element extension = genOptionalTextType(el, xData, parserInfo);
				if (extension == null) { // text item is NOT optional!
					Element complexType = genSchemaElem(el, "complexType");
					String typeName = genDeclaredName(parserInfo);
					Element simpleContent;
					if (typeName == null) {
						if (parserInfo.getParser().getNamedParams().isEmpty()
							&& (parserInfo.getInfo() == null
							|| parserInfo.getInfo().isEmpty())) {
							simpleContent =
								genSchemaElem(complexType, "simpleContent");
							extension = genSchemaElem(simpleContent,
								"extension");
							extension.setAttribute("base",
								SCHEMA_PFX+parserInfo.getParser().parserName());
						} else {
							typeName =
								createSchemaTypeName(el, xel.getLocalName());
							if (!targetNs.isEmpty()) {
								simpleContent =
									genSchemaElem(complexType,"simpleContent");
								extension =
									genSchemaElem(simpleContent,"extension");
								extension.setAttribute("xmlns", targetNs);
							}
						}
					}
					if (typeName != null) {
						Element tpel = _types==null ? schema : _types;
						if (findSchematype(tpel, typeName) == null) {
							simpleType = genSchemaElem(tpel, "simpleType");
							simpleType.setAttribute("name", typeName);
							genRestrictions(simpleType, parserInfo);
						}
						simpleContent =
							genSchemaElem(complexType, "simpleContent");
						extension = genSchemaElem(simpleContent, "extension");
						extension.setAttribute("base", typeName);
						if (!targetNs.isEmpty()) {
							extension.setAttribute("xmlns", targetNs);
						}
					}
				}
				addAttrs(extension, attrs); // add declaration of attributes.
			}
			return el;
		}
		Element complt = genSchemaElem(el, "complexType");
		if (children.length > 0) {
			XMNode x = children[0];
			if (x.getKind()==XMNode.XMMIXED
				&& ((XMSelector) x).getEndIndex()==children.length-1) {
				// try tu generate xs:all
				boolean allPossible = true;
				for (int i = 1; i < children.length; i++) {
					XMNode y = children[i];
					if (y.getKind() == XMNode.XMELEMENT) {
							XMOccurrence occ = y.getOccurence();
							if (occ.minOccurs() > 1 || occ.maxOccurs() > 1) {
								allPossible = false;
								break;
							}
					} else {
						break;
					}
				}
				if (allPossible) { // generate xs:all
					Element all = genSchemaElem(complt, "all");
					for (int i = 1; i < children.length; i++) {
						XMNode y = children[i];
						if (y.getKind() == XMNode.XMELEMENT) {
							genElem(all, (XMElement) y);
						}
					}
					addAttrs(complt, attrs);
					return el;
				}
			}
			switch (x.getKind()) {
				case XMNode.XMCHOICE:
				case XMNode.XMMIXED:
				case XMNode.XMSEQUENCE:
					if (((XMSelector) x).getEndIndex() == children.length - 1) {
						genGroup(complt,
							complt, (XMSelector) children[0], children, 0);
						addAttrs(complt, attrs);
						return el;
					}
			}
			Element seq = genSequenceElement(complt);
			genSequence(complt, seq, children, -1, children.length);
		}
		addAttrs(complt, attrs);
		return el;
	}

	/** Generate a new unique XML schema file name.
	 * @return new unique XML schema file name.
	 */
	private String genNewName() {
		for (int i = 1;;i++) {
			String s = _rootName + "_" + (i<10?"0":"") + i;
			if (!_xsdSources.containsKey(s)) {
				return s;
			}
		}
	}

	/** Add schema to the map of schema.
	 * @param name name of item.
	 * @param schema the XML element with schema.
	 */
	public final void addSchema(final String name, final Element schema) {
		if (_xsdSources.containsKey(name)) {
			//XML schema "&{0}" already exists
			throw new SRuntimeException(XDCONV.XDCONV207, name);
		}
		_xsdSources.put(name, schema);
	}

	/** Create new element with XML schema namespace and given name and add
	 * it to the parent node (if parent is not null).
	 * @param parent parent node.
	 * @param name local name of element to be created.
	 * @return created element.
	 */
	private Element genSchemaElem(final Element parent, final String name) {
		Element result = _doc.createElementNS(
			XMLConstants.W3C_XML_SCHEMA_NS_URI, SCHEMA_PFX + name);
		if (parent != null) {
			parent.appendChild(result);
		}
		return result;
	}


	/** Create new Schema element.
	 * @return created element.
	 */
	private Element genNewSchema() {
		Element schema = genSchemaElem(null, "schema");
		schema.setAttribute("elementFormDefault", "qualified");
		schema.setAttribute("attributeFormDefault", "unqualified");
		if (_typesName!= null) {
			schema.setAttribute("xmlns", USERTYPES_URI);
			Element imprt = genSchemaElem(schema, "import");
			imprt.setAttribute("namespace", USERTYPES_URI);
			imprt.setAttribute("schemaLocation", _typesName + ".xsd");
		}
		return schema;
	}

	/** Create new Schema element and put it to the map of schema.
	 * @param outName name of item in the map.
	 * @return created element.
	 */
	private Element genNewSchema(final String outName) {
		Element schema = genNewSchema();
		addSchema(outName, schema);
		return schema;
	}

	/** Find schema with given namespace in the map of schema.
	 * @param uri namespace to be found.
	 * @return found schema or null.
	 */
	private String findSchemaItem(final String uri) {
		for (Map.Entry en: _xsdSources.entrySet()) {
			Element schema = en.getValue();
			String ns = schema.getAttribute("targetNamespace");
			if ((ns.isEmpty()&&(uri==null||uri.isEmpty())) || ns.equals(uri)) {
				return en.getKey();
			}
		}
		return null;
	}

	/** Run XML schema generator.
	 * @param xp compiled XDPool.
	 * @param xdName name of root X-definition.
	 * @param modelName name of the root model. May be null, then all values
	 * from "xs:root" parameter are used to create models.
	 * @param outName name of root XML schema file.
	 * @param outType name of XML schema file with type declarations (may be
	 * null, then declared simple types are generated to the file with model).
	 * @param genInfo switch if generate annotation with documentation.
	 * @param genXdateOutFormat if true, from the xdatetime method the outFormat
	 * parameter (the second sequential) is used as mask to validate datetime.
	 * @return map with names of XML schema files and corresponding Elements.
	 */
	public static Map genSchema(final XDPool xp,
		final String xdName,
		final String modelName,
		final String outName,
		final String outType,
		final boolean genInfo,
		final boolean genXdateOutFormat) {
		String xname = xdName == null ? xp.getXMDefinitionNames()[0] : xdName;
		XMDefinition xdef = xp.getXMDefinition(xname);
		if (xdef == null) {
			//Can't find an X-definition&{0}{ "}{"}
			throw new SRuntimeException(XDCONV.XDCONV201, xname);
		}
		String mname;
		XMElement[] roots = null;
		if (modelName == null || modelName.isEmpty()) {
			roots = xp.getXMDefinition(xname).getRootModels();
			if (roots != null && roots.length > 0) {
				mname = roots[0].getLocalName();
			} else {
				//XML model not specified
				throw new SRuntimeException(XDCONV.XDCONV202);
			}
		} else {
			roots = xp.getXMDefinition(xname).getModels();
			String mURI = null;
			mname = null;
			for (XMElement xel : roots) {
				if (modelName.equals(xel.getName())) {
					mname = xel.getLocalName();
					mURI = xel.getNSUri();
					break;
				}
			}
			if (mname == null) {
				//Can't find root model&{0}{ "}{"}
				throw new SRuntimeException(XDCONV.XDCONV203, modelName);
			}
			XMDefinition xmdef = xp.getXMDefinition(xname);
			roots = new XMElement[1];
			roots[0] = xmdef.getModel(mURI, mname);
		}
		String oname = outName == null ? mname.replace(':', '_') : outName;
		String otype = outType;
		if (otype != null && !otype.isEmpty()) {
			if ((modelName == null || modelName.isEmpty())
				&& (outName == null || outName.isEmpty())) {
				roots = null;
			}
		} else {
			otype = null;
			if (mname == null || mname.isEmpty()) {
				//The name of xsd file is missing
				throw new SRuntimeException(XDCONV.XDCONV204);
			}
		}
		Xd2Xsd generator = new Xd2Xsd(xp, otype, genInfo, genXdateOutFormat);
		if (roots != null) {
			Element schema = generator.genNewSchema(oname);
			XMElement xmel = roots[0];
			String nsUri = xmel.getNSUri();
			if (nsUri != null && !nsUri.isEmpty()) {
				schema.setAttribute("targetNamespace", nsUri);
			}
			generator.genElem(schema, xmel);
			for (int i = 1; i < roots.length; i++) {
				generator.genElem(schema, roots[i]);
			}
		}
		return generator._xsdSources;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy