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

org.xdef.impl.compile.PreReaderXML Maven / Gradle / Ivy

There is a newer version: 42.2.13
Show newest version
package org.xdef.impl.compile;

import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.xdef.XDConstants;
import org.xdef.XDPool;
import org.xdef.impl.XConstants;
import org.xdef.impl.XDefinition;
import org.xdef.impl.xml.KParsedAttr;
import org.xdef.impl.xml.KParsedElement;
import org.xdef.msg.SYS;
import org.xdef.msg.XDEF;
import org.xdef.msg.XML;
import org.xdef.sys.SBuffer;
import org.xdef.sys.SPosition;
import org.xdef.sys.SRuntimeException;
import org.xdef.sys.SThrowable;
import org.xdef.sys.StringParser;
import org.xdef.xml.KXmlUtils;

/** Reads source X-definitions and prepares list of PNodes with X-definitions
 * from XML source data.
 * @author Trojan
 */
class PreReaderXML extends XmlDefReader implements PreReader {
	/** Actual node */
	private PNode _actPNode;
	/** includes. */
	private Element _includeElement;
	/** Nesting level of XML node. */
	private int _level;
	/** Instance of PreCompiler. */
	private final XPreCompiler _pcomp;

	/** Creates a new instance of XPreCompiler.
	 * @param pcomp XPreCompiler.
	 */
	PreReaderXML(final XPreCompiler pcomp) {super(); _pcomp = pcomp;}

	@Override
	/** This method is called after all attributes of the current element
	 * attribute list was reached. The implementation may check the list of
	 * attributes and to invoke appropriate actions. The method is invoked
	 * when parser reaches the end of the attribute list.
	 * @param parsedElem contains name of the element, name space URI and
	 * the list of attributes.
	 */
	public final void elementStart(final KParsedElement parsedElem) {
		String qName = parsedElem.getParsedName();
		if (_includeElement == null) {
			if (_actPNode != null && _actPNode._value != null &&
				_actPNode._value.getString().length() > 0) {
				processText();
			}
			_actPNode = new PNode(qName,
				parsedElem.getParsedNameSourcePosition(),
				_actPNode,
				_actPNode==null? (byte) 0 : _actPNode._xdVersion,
				"1.1".equals(getXmlVersion())
					? StringParser.XMLVER1_1 : StringParser.XMLVER1_0);
		}
		String elemPrefix;
		String elemLocalName;
		int ndx;
		if ((ndx = qName.indexOf(':')) >= 0) {
			elemPrefix = qName.substring(0, ndx);
			_actPNode._localName = elemLocalName = qName.substring(ndx + 1);
		} else {
			elemPrefix = "";
			_actPNode._localName = elemLocalName = qName;
		}
		_actPNode._nsURI = parsedElem.getParsedNSURI();
		if (_level == -1) {
			_actPNode._xpathPos = qName + "[1]";
			String uri = parsedElem.getParsedNSURI();
			if ("def".equals(elemLocalName)
				|| "lexicon".equals(elemLocalName)
				|| "thesaurus".equals(elemLocalName)//&&_actPNode._xdVersion==31
				|| "declaration".equals(elemLocalName)
				|| "component".equals(elemLocalName)
				|| "BNFGrammar".equals(elemLocalName)
				|| "collection".equals(elemLocalName))  {
				String projectNS; // = XDConstants.XDEFxxx_NS_URI;
				KParsedAttr ka;
				byte ver;
				if ((ka = parsedElem.getAttrNS(
						XDConstants.XDEF31_NS_URI, "metaNamespace")) != null
					|| (ka = parsedElem.getAttrNS(
						XDConstants.XDEF32_NS_URI, "metaNamespace")) != null
					|| (ka = parsedElem.getAttrNS(
						XDConstants.XDEF40_NS_URI, "metaNamespace")) != null
					|| (ka = parsedElem.getAttrNS(
						XDConstants.XDEF41_NS_URI, "metaNamespace")) != null
					|| (ka = parsedElem.getAttrNS(
						XDConstants.XDEF42_NS_URI, "metaNamespace")) != null) {
					projectNS = ka.getValue().trim();
					ver = XDConstants.XDEF31_NS_URI.equals(ka.getNamespaceURI())
						? XConstants.XD31
						: XDConstants.XDEF32_NS_URI.equals(ka.getNamespaceURI())
						? XConstants.XD32
						: XDConstants.XDEF40_NS_URI.equals(ka.getNamespaceURI())
						? XConstants.XD40 : XConstants.XD41;
					try {
						if (projectNS.isEmpty()) {
							throw new RuntimeException(); // just force error
						}
						new URI(projectNS); // just to check projectNS validity
					} catch (RuntimeException | URISyntaxException ex) {
						//Attribute 'metaNamespace' must contain a valid URI
						error(ka.getPosition(), XDEF.XDEF253);
					}
					parsedElem.remove(ka);
				} else {
					if (XDConstants.XDEF31_NS_URI.equals(uri)
						|| XDConstants.XDEF32_NS_URI.equals(uri)
						|| XDConstants.XDEF40_NS_URI.equals(uri)
						|| XDConstants.XDEF41_NS_URI.equals(uri)
						|| XDConstants.XDEF42_NS_URI.equals(uri)) {
						ver = XDConstants.XDEF31_NS_URI.equals(uri)
							? XConstants.XD31
							: XDConstants.XDEF32_NS_URI.equals(uri)
							? XConstants.XD32
							: XDConstants.XDEF40_NS_URI.equals(uri)
							? XConstants.XD40
							: XDConstants.XDEF41_NS_URI.equals(uri)
							? XConstants.XD41 : XConstants.XD42;
						projectNS = uri;
					} else {
						//Namespace of X-definitions is required
						error(_actPNode._name, XDEF.XDEF256);
						projectNS = XDConstants.XDEF42_NS_URI;
						ver = XConstants.XD42;
					}
				}
				if (_pcomp.isChkWarnings()&&"thesaurus".equals(elemLocalName)) {
					//&{0} is deprecated.&{1}{ Please use }{ instead.}
					warning(_actPNode._name,XDEF.XDEF998,"thesaurus","lexicon");
				}
				_actPNode._xdVersion = ver;
				_pcomp.setURIOnIndex(0, projectNS);
			} else {
				_pcomp.setURIOnIndex(0, uri);
				//X-definition or X-collection expected
				error(_actPNode._name, XDEF.XDEF255);
			}
		} else {
			_actPNode._parent.addChildNode(_actPNode);
		}
		for (int i = 0, max = parsedElem.getLength(); i < max; i++) {
			KParsedAttr ka = parsedElem.getAttr(i);
			String key = ka.getName();
			String value = ka.getValue();
			if (key.startsWith("xmlns")) { //addAttr namespace URI to the list.
				int nsndx = _pcomp.setNSURI(value.trim());
				if (key.length() == 5) { //default prefix
					_actPNode._nsPrefixes.put("", nsndx);
				} else if (key.charAt(5) == ':') { //prefix name
					_actPNode._nsPrefixes.put(key.substring(6), nsndx);
				}
			} else if ("collection".equals(elemLocalName) &&
				key.startsWith("impl-")) {//continue; ignore, just documentation
			} else {
				PAttr item = new PAttr(key,
					new SBuffer(value, ka.getPosition()), null, -1);
				_actPNode.setAttr(item);
				if ((ndx = key.indexOf(':')) >= 0) {
					String prefix = key.substring(0, ndx);
					item._localName = key.substring(ndx + 1);
					Integer nsndx = _actPNode._nsPrefixes.get(prefix);
					if (nsndx == null) {
						String u = ka.getNamespaceURI();
						if (u != null) {
							nsndx = _pcomp.setNSURI(u);
							_actPNode._nsPrefixes.put(prefix, nsndx);
						}
					}
					if (nsndx != null) {
						item._nsURI = _pcomp.getNSURI(nsndx);
						if ((item._nsindex=nsndx) == XPreCompiler.NS_XDEF_INDEX
							&& "script".equals(item._localName)) {
							XScriptParser xp =
								new XScriptParser(_actPNode._xmlVersion);
							xp.setSource(new SBuffer(value, ka.getPosition()),
								_actPNode._xdef == null ? null
									: _actPNode._xdef.getName(),
								null,_actPNode._xmlVersion, item._xpathPos);
							xp.skipBlanksAndComments();
							if (xp.isToken("template")) {
								xp.skipBlanksAndComments();
								if (!xp.eos() && !xp.isChar(';')) {
									xp.error(XDEF.XDEF425);//Script error
								}
								_actPNode._template = true;
							}
						}
					} else {
						item._nsindex = -1;
					}
				} else {
					item._localName = key;
					item._nsindex = -1;
				}
			}
		}
		Integer nsuriIndex = _actPNode._nsPrefixes.get(elemPrefix);
		if (nsuriIndex != null) {
			int urindx;
			if (((urindx = nsuriIndex) == XPreCompiler.NS_XINCLUDE_INDEX)) {
				String nsuri = _pcomp.getNSURI(urindx);
				Element elem;
				if (_includeElement == null) {
					elem = _includeElement = KXmlUtils.newDocument(nsuri,
						_actPNode._name.getString(), null).getDocumentElement();
				} else {
					elem = _includeElement.getOwnerDocument().createElementNS(
						nsuri, _actPNode._name.getString());
					_includeElement.appendChild(elem);
				}
				for (PAttr aval: _actPNode.getAttrs()) {
					if (aval._nsindex < 0) {
						elem.setAttribute(aval._name, aval._value.getString());
					} else {
						elem.setAttributeNS(_pcomp.getNSURI(aval._nsindex),
							aval._name,
							aval._value.getString());
					}
				}
				return;
			} else {
				_actPNode._nsindex = urindx;
			}
		}
		if (_level++ == -1) { // root level
			if ("collection".equals(elemLocalName)) {
				_pcomp.processIncludeList(_actPNode);
				_pcomp.reportNotAllowedAttrs(_actPNode);
				_pcomp.getPCollections().add(_actPNode);
				_level = -1; // collection must be -1!
			} else if ("BNFGrammar".equals(elemLocalName)) {
				 _pcomp.getPBNFs().add(_actPNode);
			} else if ("lexicon".equals(elemLocalName)
				|| ("thesaurus".equals(elemLocalName)
					&&_actPNode._xdVersion <= 31)) {
				_pcomp.getPLexiconList().add(_actPNode);
			} else if ("declaration".equals(elemLocalName)) {
				_pcomp.getPDeclarations().add(0, _actPNode);
			} else if ("component".equals(elemLocalName)) {
				_pcomp.getPComponents().add(0, _actPNode);
			} else { // def
				String defName = getNameAttr(_actPNode, false, true);
				if (defName == null) {
					defName = "";
				}
				// Because there is not yet connected an X-definition to
				// the PNode we create a dumy one in fact just to store
				// the X-definition name (we nead it to be able to compile
				// internal declarations, BNGGrammars, components and
				// lexicon items).
				_actPNode._xdef = new XDefinition(defName,
					null, _actPNode._nsURI, null, _actPNode._xmlVersion);
				_pcomp.processIncludeList(_actPNode);
				// check duplicate of X-definition
				for (PNode pn: _pcomp.getPXDefs()) {
					if (defName.equals(pn._xdef.getName())) {
						XScriptParser xp =
							new XScriptParser(_actPNode._xmlVersion);
						xp.setSource(_actPNode._name,
							_actPNode._xdef == null ? null
								: _actPNode._xdef.getName(),
							null,_actPNode._xmlVersion,_actPNode._xpathPos);
						if (defName.length() == 0) {
							//Only one X-definition in the compiled XDPool
							// may be without name
							xp.error(_actPNode._name, XDEF.XDEF212);
						} else {
							//X-definition '&{0}' already exists
							xp.error(_actPNode._name,XDEF.XDEF268, defName);
						}
						defName = null;
						break;
					}
				}
				if (defName != null) {
					_pcomp.getPXDefs().add(_actPNode);
				}
			}
		}
	}

	/** Get "name" (or "prefix:name") of node.
	 * If the argument required is set to true put error message that
	 * required attribute is missing.
	 * @param pnode PNode where the attribute "name" is required.
	 * @param required if true the attribute is required.
	 * @param remove if true the attribute is removed.
	 * @return the name or null.
	 */
	private String getNameAttr(final PNode pnode,
		final boolean required,
		final boolean remove) {
		PAttr pa = _pcomp.getXdefAttr(pnode, "name", required, remove);
		if (pa == null) {
			// if required do not return null!
			return required ? ("_UNKNOWN_REQUIRED_NAME_") : null;
		}
		String name = pa._value.getString();
		if (name.length() == 0) {
			//Incorrect name
			error(pa._value, XDEF.XDEF258);
			return "__UNKNOWN_ATTRIBUTE_NAME_";
		}
		if (!PreReaderXML.chkDefName(name, pnode._xmlVersion)) {
			error(pa._value, XDEF.XDEF258); //Incorrect name
			return "__UNKNOWN_INCORRECT_NAME_";
		}
		return name;
	}

	@Override
	/** This method is invoked when parser reaches the end of element. */
	public final void elementEnd() {
		if (_includeElement != null) {
			String ns = _includeElement.getPrefix();
			ns = ns == null || ns.length() == 0 ? "xmlns" : "xmlns:" + ns;
			NamedNodeMap nm = _includeElement.getAttributes();
			if (nm.getLength() > 0) { //other attributes
				for (int i = 0; i < nm.getLength(); i++) {
					Node n = nm.item(i);
					String name = n.getNodeName();
					if (!"href".equals(name) && !"parse".equals(name)
						&& !ns.equals(name)) {
						//Xinclude - unknown attribute &{0}
						error(_actPNode._name, XML.XML305, n.getNodeName());
					}
				}
			}
			_includeElement = null;
			_actPNode = _actPNode._parent;
			return;
		}
		if (_actPNode._value != null &&
			_actPNode._value.getString().length() > 0) {
			processText();
		}
		_level--;
		_actPNode = _actPNode._parent;
	}

	@Override
	/** New text value of current element parsed.
	 * @param text SBuffer with value of text node.
	 */
	public final void text(final SBuffer text) {
		if (_includeElement != null) {
			return;
		}
		if (_actPNode._template && _level > 0 &&
			_actPNode._nsindex != XPreCompiler.NS_XDEF_INDEX) {
			if (text != null && !text.getString().isEmpty()) {
				SBuffer sval = new SBuffer(text.getString(), text);
				if (_actPNode._value == null) {
					_actPNode._value = sval;
				} else {
					_actPNode._value.appendToBuffer(sval);
				}
			}
			return;
		}
		if (text == null) {
			return; // no string
		}
		String s = text.getString();
		int len = s.length() - 1;
		while (len >= 0 && s.charAt(len) <= ' ') {
			len--;
		}
		if (len < 0) {
			return; // empty string
		}
		SBuffer sb =new SBuffer(s, text);
		if (_actPNode._value == null) {
			_actPNode._value = sb;
		} else {
			if (_actPNode._value == null) {
				_actPNode._value = sb;
			} else {
				_actPNode._value.appendToBuffer(sb);
			}
		}
		if (_actPNode._nsindex == XPreCompiler.NS_XDEF_INDEX &&
			"def".equals(_actPNode._localName)) {
			XScriptParser xparser = new XScriptParser(_actPNode._xmlVersion);
			xparser.setLineInfoFlag(true);
			xparser.setReportWriter(getReportWriter());
			xparser.setSourceBuffer(_actPNode._value);
			// it still may be a comment
			if (xparser.nextSymbol() != XScriptParser.NOCHAR) {
				//Text value is not allowed here
				lightError(_actPNode._value, XDEF.XDEF260);
			}
			_actPNode._value = null;//prevent repeated message, remove this text
		}
	}

	/** Generate text node */
	private void genTextNode() {
		String name = "";
		for (String prefix : _actPNode._nsPrefixes.keySet()) {
			if (_actPNode._nsPrefixes.get(prefix)==XPreCompiler.NS_XDEF_INDEX) {
				name = prefix + ":text";
				break;
			}
		}
		PNode pn = new PNode(name,
			new SPosition(_actPNode._value),
			_actPNode,
			_actPNode._xdVersion,
			_actPNode._xmlVersion);
		pn._nsURI = _pcomp.getNSURI(XPreCompiler.NS_XDEF_INDEX);
		pn._nsindex = XPreCompiler.NS_XDEF_INDEX;
		pn._localName = "text";
		pn._value = _actPNode._value;
		_actPNode._value = null;
		_level++;
		_actPNode.addChildNode(pn);
		_level--;
	}

	private void processText() {
		if (_actPNode._template && _level > 0
			&& _actPNode._nsindex != XPreCompiler.NS_XDEF_INDEX) {
			genTextNode();
			return;
		}
		XScriptParser xparser = new XScriptParser(_actPNode._xmlVersion);
		xparser.setLineInfoFlag(true);
		xparser.setReportWriter(getReportWriter());
		xparser.setSourceBuffer(_actPNode._value);
		if (xparser.nextSymbol() == XScriptParser.NOCHAR) {
			_actPNode._value = null; // remove this text
			return;
		}
		if (_actPNode._nsindex == XPreCompiler.NS_XDEF_INDEX) {
			if ("xon".equals(_actPNode._localName) //xon
				|| "json".equals(_actPNode._localName) //json
				|| "ini".equals(_actPNode._localName)) { // ini
				if (_level != 1) {
					//JSON model can be declared only as a child of X-definition
					error(_actPNode._value, XDEF.XDEF310, _actPNode._localName);
					_actPNode._value = null;
					return;
				}
				PNode pnode = _actPNode;
				PAttr paName =  _pcomp.getXdefAttr(pnode, "name", false,true);
				String xdname = _actPNode._localName;
				SBuffer sval = paName == null ? null : paName._value;
				if (sval == null) {
					sval = new SBuffer(xdname, pnode._name);
					//The name of XON/JSON model is required
					error(pnode._name, XDEF.XDEF317, xdname);
				} else {
					String s = sval.getString().trim();
					if (!StringParser.chkNCName(s, StringParser.XMLVER1_0)) {
						//The name of {0} model "&{0}" can't contain ":"
						error(sval, XDEF.XDEF316, xdname, s);
					}
				}
				CompileXonXdef compileXon =
					new CompileXonXdef(_actPNode, sval, getReportWriter());
				pnode._xonMode = XConstants.XON_ROOT;
				if (pnode._value==null || pnode._value.getString().isEmpty()) {
					//XON/JSON model is missing in JSON definition
					error(pnode._name, XDEF.XDEF315,"&{xpath}"+pnode._xpathPos);
					return;
				}
				for (PAttr pattr:  pnode.getAttrs()) {
					//Attribute '&{0}' not allowed here
					error(pattr._value, XDEF.XDEF254, pattr._name);
				}
				String anyXpos = compileXon.genXdef(pnode,
					xdname.equals("json") ? "xon" : xdname,
					sval, // model source code
					_pcomp.getReportWriter());
				pnode._name = paName!=null ? paName._value : new SBuffer("");
				pnode._localName = paName!=null ? paName._value.getString(): "";
				pnode._nsURI = null; // set no namespace
				pnode._nsindex = -1;
				pnode._xonMode = XConstants.XON_ROOT;
				if (anyXpos != null) { // generate models for %anyObj
					int ndx = anyXpos.indexOf('#');
					anyXpos = anyXpos.substring(ndx + 1);
					for (PNode  p:_actPNode._parent.getChildNodes()) {
						if (anyXpos.equals(p._localName)) {
							return; // already generated
						}
						if (p._xonMode > 0 && "choice".equals(p._localName)) {
							for (PAttr pa: p.getAttrs()) {
								if ("name".equals(pa._localName)
									&& anyXpos.equals(pa._value.getString())) {
									return; // already generated
								}
							}
						}
					}
					/** Prepare instance of CompileXonXdef. */
					compileXon.genXonAnyModels(_actPNode, anyXpos);
				}
				return;
			} else if ("text".equals(_actPNode._localName)
				|| "BNFGrammar".equals(_actPNode._localName)
				|| "lexicon".equals(_actPNode._localName)
				|| "thesaurus".equals(_actPNode._localName)
//					&& _actPNode._xdVersion == 31
				|| "declaration".equals(_actPNode._localName)
				|| "component".equals(_actPNode._localName)
				|| "macro".equals(_actPNode._localName)) {
				return; //text is processed in the pnode
			} else if (!"mixed".equals(_actPNode._localName)
				&& !"choice".equals(_actPNode._localName)
				&& !"list".equals(_actPNode._localName)
//				&& !"PI".equals(_actPNode._localName) //TODO
//				&& !"comment".equals(_actPNode._localName) //TODO
//				&& !"document".equals(_actPNode._localName) //TODO
//				&& !"value".equals(_actPNode._localName) //TODO
//				&&!"attlist".equals(_actPNode._localName) //TODO
				&& !"sequence".equals(_actPNode._localName)
				&& !"any".equals(_actPNode._localName)) {
				//Unknown element from X-definotion namespace: '&{0}'
				lightError(_actPNode._value, XDEF.XDEF259,_actPNode._localName);
				_actPNode._value = null; //prevent repeated message
				return;
			}
		}
		if (_level == 0) {
			//Text value not allowed here
			lightError(_actPNode._value, XDEF.XDEF260);
			_actPNode._value = null; //prevent repeated message
		} else {
			genTextNode(); //generate text node
		}
	}

	/** Parse InputStream source X-definition and addAttr it to the set
	 * of definitions.
	 * @param in input stream with the X-definition.
	 * @param srcName name of source data used in reporting (SysId) or
	 * null.
	 */
	public final void parseStream(final InputStream in, final String srcName) {
		if (_pcomp.getSources().contains(in)) {
			return;
		}
		_pcomp.getSources().add(in);
		_sysId = srcName;
		_level = -1;
		_actPNode = null;
		try {
			setReportWriter(_pcomp.getReportWriter());
			doParse(in, srcName);
		} catch (RuntimeException ex) {
			throw ex;
		} catch (Exception ex) {
			_actPNode = null; //just let gc to do the job
			if (_pcomp.getDispalyMode() > XDPool.DISPLAY_FALSE) {
				if (!(ex instanceof SThrowable)) {
					getReportWriter().error(SYS.SYS066, //Internal error&{0}{: }
						"when parsing document\n" + ex);
				}
			} else {
				if (ex instanceof SThrowable &&
					"SYS012".equals(((SThrowable) ex).getMsgID())) {
					throw (SRuntimeException) ex; //Errors detected&{0}{: }
				} else {
					//Internal error: &{0}
					throw new SRuntimeException(SYS.SYS066,
						ex, "when parsing document\n" + ex);
				}
			}
		}
		_actPNode = null; //just let gc to do the job
	}

	/** Check if the name of X-definition is OK.
	 * @param name name of X-definition
	 * @return true if the name of X-definition is OK.
	 */
	private static boolean chkDefName(final String name, byte xmlVersion) {
		if (name.length() == 0) {
			return true; //nameless is also name
		}
		if (StringParser.getXmlCharType(name.charAt(0),  xmlVersion) !=
			StringParser.XML_CHAR_NAME_START) {
			return false;
		}
		char c;
		boolean wasColon = false;
		for (int i = 1; i < name.length(); i++) {
			if (StringParser.getXmlCharType(c = name.charAt(i),  xmlVersion) !=
				StringParser.XML_CHAR_NAME_START && (c  < '0' && c > '9')) {
				if (!wasColon && c == ':') { // we allow one colon inside name
					wasColon = true;
					if (i + 1 < name.length()
						&& StringParser.getXmlCharType(
							name.charAt(++i), xmlVersion)
						!= StringParser.XML_CHAR_NAME_START){//must follow name
						continue;
					}
				}
				return false;
			}
		}
		return true;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy