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

org.xdef.impl.ChkElement Maven / Gradle / Ivy

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

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.XMLConstants;
import org.w3c.dom.Attr;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.xdef.XDDebug;
import org.xdef.XDParseResult;
import org.xdef.XDUniqueSetKey;
import org.xdef.XDValue;
import static org.xdef.XDValueID.XD_NULL;
import static org.xdef.XDValueID.XD_PARSERESULT;
import static org.xdef.XDValueID.XX_ATTR;
import static org.xdef.XDValueID.XX_ELEMENT;
import static org.xdef.XDValueID.XX_TEXT;
import static org.xdef.XDValueID.X_UNIQUESET;
import static org.xdef.XDValueID.X_UNIQUESET_M;
import org.xdef.XDValueType;
import static org.xdef.XDValueType.XXATTR;
import static org.xdef.XDValueType.XXELEMENT;
import static org.xdef.XDValueType.XXTEXT;
import org.xdef.component.XComponent;
import org.xdef.impl.code.CodeUniqueset;
import org.xdef.impl.code.DefBoolean;
import org.xdef.impl.code.DefParseResult;
import org.xdef.impl.xml.KNamespace;
import org.xdef.xon.XonTools;
import org.xdef.model.XMData;
import org.xdef.model.XMElement;
import org.xdef.model.XMNode;
import static org.xdef.model.XMNode.XMCHOICE;
import static org.xdef.model.XMNode.XMELEMENT;
import static org.xdef.model.XMNode.XMMIXED;
import static org.xdef.model.XMNode.XMSELECTOR_END;
import static org.xdef.model.XMNode.XMSEQUENCE;
import static org.xdef.model.XMNode.XMTEXT;
import org.xdef.msg.SYS;
import org.xdef.msg.XDEF;
import org.xdef.proc.XXData;
import org.xdef.proc.XXElement;
import org.xdef.proc.XXNode;
import org.xdef.sys.ArrayReporter;
import org.xdef.sys.Report;
import org.xdef.sys.SRuntimeException;
import org.xdef.sys.SUtils;
import org.xdef.xml.KXmlUtils;
import static org.xdef.xon.XonNames.X_ARRAY;
import static org.xdef.xon.XonNames.X_KEYATTR;
import static org.xdef.xon.XonNames.X_MAP;
import static org.xdef.xon.XonNames.X_VALATTR;
import static org.xdef.xon.XonNames.X_VALUE;

/** Provides validation of input data or it can be used as base for construction
 * of XML objects according to a X-definition.
 *  This code is nasty code in some parts, should be written better!
 * @author Vaclav Trojan
 */
public final class ChkElement extends ChkNode implements XXElement, XXData {

	/** The set containing marked unique sets. */
	final Set _markedUniqueSets = new HashSet<>();
	/** Switch if the actual reporter is cleared on invoked action. */
	final private boolean _clearReports;
	/** Array of bound keys.*/
	XDUniqueSetKey[] _boundKeys;
	/** Model of the processed data object.*/
	XMData _xdata;
	/** XON Map. */
	Map _xonMap;
	/** XON Array. */
	List _xonArray;
	/** XON item name. */
	String _xonKey;
	/** XON item value. */
	Object _xonValue;
	/** Index to actual X-definition. */
	int _actDefIndex;
	/** Index to next X-definition. */
	int _nextDefIndex;
	/** Text value (of actual text node or attribute). It is used
	 * for communication with the XScript interpreter.
	 * Important note: it should be cleared after invocation of
	 * external methods - to allow gc to do the job!
	 */
	private String _data;
	/** Element value used for X-Script code.
	 * Important note: it should be cleared after invocation of
	 * external methods - to allow garbage collector to do the job!
	 */
	private Element _elemValue;
	/** The name of actually processed attribute for communication with
	 *  the X-Script interpreter. */
	private String _attName;
	/** The namespace URI of actually processed attribute for communication with
	 *  the X-Script interpreter. */
	private String _attURI;
	/** List of names of attributes. */
	private Set _attNames;
	/** The Map with child XPath occurrences. */
	private final Map _xPosOccur;
	/** Array of X-definitions. */
	private XNode[] _defList;
	/** Array of occurrence counters. */
	private int[] _counters;
	/** Number of text nodes found. */
	private int _numText;
	/** Flag this element should be forgotten. */
	private boolean _forget;
	/** If true ignore this element and all nested nodes. */
	private boolean _ignoreAll;
	/** Actual selector. */
	SelectorState _selector;
	/** If true the element was set to nil. */
	private boolean _nil;
	/** The list of child check elements. */
	List _chkChildNodes;
	/** If true the element attributes had been checked. */
	private boolean _attsChecked;
	 /** mode: 'C' - comment, 'E' - element, 'A' - attribute, 'T' - text,
	 * 'D' - document, 'P' - processing instruction,'U' undefined. */
	private byte _mode;
	/** XComponent if exists or null. */
	private XComponent _xComponent;

	/** Creates a new empty instance of ChkElement - just for internal use.
	 * @param xelem XElement from X-definition.
	 * @param parent ChkNode parent.
	 * @param element element with attributes.
	 * @param ignoreAll if true ignore this and all child nodes.
	 */
	ChkElement(final ChkNode parent,
		Element element,
		final XElement xelem,
		boolean ignoreAll) {
		super(element==null ? xelem.getName(): element.getNodeName(),parent);
//		_sourceElem=_elemValue=null; _selector=null; _nil=false;//Java makes it!
//		Arrays.fill(_counters, 0); _nextDefIndex=_numText=0; //Java makes it!
//		_boundKeys = null; _nextDefIndex=_numText=0; //Java makes it!
		_clearReports = xelem._clearReports != 0
			? xelem._clearReports == (byte) 'T'
			: xelem.getXDPool().isClearReports();
		_element = element;
		_ignoreAll = ignoreAll || xelem.isIgnore() || xelem.isIllegal();
		if (xelem.isIgnore() || xelem.isIllegal()) {
			_forget = true;
		}
		_xElement = xelem;
		_xPosOccur = new LinkedHashMap<>();
		_xPos = _parent.getXPos() + '/' + _name;
		if (_parent.getParent() != null) {
			int xPosCnt = getElemXPos(((ChkElement)_parent)._xPosOccur, _xPos);
			_xPos += '[' + String.valueOf(xPosCnt)+ ']';
		}
		_errCount=getReporter().getErrorCount()+_scp._reporter.getErrorCount();
		_defList = _xElement._childNodes;
		_actDefIndex = -1; //index of actual X-definition
		_counters = new int[_defList.length + 1]; //one more for '*'
		_chkChildNodes = new ArrayList<>();
		_attNames = new HashSet<>();
		if (ignoreAll) {
			_element = null;
		} else if (_element != null) {
			Element el;
			if (_parent._parent == null) { //root element
				el = _rootChkDocument._doc.getDocumentElement();
				if (el == null) {
					_rootChkDocument._doc.appendChild(_element);
				} else if (el != _element) {
					el.appendChild(_element);
				}
			} else if ((el = _parent.getElement()) != null) {
				el.appendChild(_element);
			}
			if (_xElement._nillable == 'T'
				&& "true".equals(_element.getAttributeNS(
					XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil"))) {
				_nil = true;
			}
		}
		if (!_ignoreAll && _element != null) {
			if (_xElement._xon > 0) { //XON
				if (_element.hasAttribute(X_KEYATTR)) {
					_xonKey = XonTools.xmlToJName(
						_element.getAttribute(X_KEYATTR));
				}
				if (X_MAP.equals(_element.getLocalName())) {
					_xonMap = new LinkedHashMap<>();
				} else if (X_ARRAY.equals(_element.getLocalName())) {
					_xonArray = new ArrayList<>();
				}
			}
			if ((_xComponent = _parent.getXComponent()) != null) {// X-component
				_xComponent = _xComponent.xCreateXChild(this);
			}
		}
	}

	/** Execute X-script from given address (with given type).
	 * @param addr address of script.
	 * @param type type of model ('E' - element, 'A' - attribute, 'T' text,
	 * otherwise 'U').
	 */
	final XDValue exec(final int addr, final byte type) {
		if (addr < 0) {
			return null;
		}
		setXXType(type);
		return _scp.exec(addr, this);
	}

	 /** Set mode: 'C' - comment, 'E' - element, 'A' - attribute, 'T' - text,
	 * 'D' - document, 'P' - processing instruction,'U' undefined. */
	final void setXXType(final byte mode) {_mode = mode;}

	 /** Get mode: 'C' - comment, 'E' - element, 'A' - attribute, 'T' - text,
	 * 'D' - document, 'P' - processing instruction,'U' undefined.
	 * @return mode.
	 */
	public final byte getXXType() {return _mode;}

	/** Set name of the actually processed attribute or Text node.
	 * @param name The name of the actually processed attribute or text node.
	 */
	final void setProcessedAttrName(final String name) {_attName = name;}

	/** Set name of the actually processed attribute or Text node.
	 * @param name The name of the actually processed attribute or text node.
	 */
	final void setProcessedAttrURI(final String uri) {_attURI = uri;}

	/** Get offset of model variable.
	 * @param name name of model variable.
	 * @return offset of model variable or -1 if variable is not declared.
	 */
	private int findModelVariableOffset(final String name) {
		if (_xElement._vartable == null) {
			return -1;
		}
		XVariable v = _xElement._vartable.getXVariable(name);
		return v == null || !v.isInitialized() ? -1 : v.getOffset();
	}

	/** Create check element object.
	 * @param element The element with attributes.
	 * @return created check element object.
	 */
	final ChkElement createChkElement(final Element element) {
		_data = null;
		_parseResult = null;
		if (_nil) {
			//Not allowed element '&{0}'
			error(XDEF.XDEF501, element.getNodeName());
			ChkElement result = new ChkElement(this,
				element, _xElement.createAnyDefElement(), true);
			_chkChildNodes.add(result);
			return result;
		}
		boolean appended = false;
		Element el;
		if ((el = getElement()) != null) {
			//let's append it for a while to be able to execute XPath
			el.appendChild(element);
			appended = true;
		}
		int nextDefIndex = _nextDefIndex; //save index for moreElement case
		int actDefIndex = _actDefIndex;
		ChkElement result = (ChkElement) findXNode(element);
		XMData textcontent =_xElement.getDefAttr("$textcontent",-1);
		if (appended && result == null && textcontent == null) {
			el.removeChild(element);
		} else if (result != null && (result.getXMElement().isIgnore()
			|| result.getXMElement().isIllegal())) {
			if (result.getXMElement().isIllegal()) {
				putTemporaryReport(Report.error( //Illegal element '&{0}'
					XDEF.XDEF557, element.getNodeName()));
				if (_xElement._onIllegalElement >= 0) {
					if (_clearReports) {
						clearTemporaryReporter();
					}
					exec(_xElement._onIllegalElement, (byte) 'E');
					copyTemporaryReports();
				}
			}
			result = new ChkElement(this,
				el, _xElement.createAnyDefElement(), true);
			if (el != null) {
				el.removeChild(element);
			}
		}
		if (result == null) {
			if (_xElement._moreElements!='T' && _xElement._moreElements!='I') {
				// Illegal element
				debugXPos(XDDebug.ONILLEGALELEMENT);
				if (_xElement._onIllegalElement >= 0) {
					_elemValue = _element;
					putTemporaryReport(Report.error(//Not allowed element '&{0}'
						XDEF.XDEF501, element.getNodeName()));
					if (_clearReports) {
						clearTemporaryReporter();
					}
					exec(_xElement._onIllegalElement, (byte) 'E');
					copyTemporaryReports();
				} else if (textcontent == null) {
					if (_xElement._xon > 0) {
						Node n = element.getAttributeNode(X_KEYATTR);
						//Not allowed item&{1}{ "}{"} in &{0}
						error(XDEF.XDEF507, _xElement.getLocalName(),
							n==null ? null : n.getNodeValue());
					} else {
						//Not allowed element '&{0}'
						error(XDEF.XDEF501, element.getNodeName());
					}
				}
				result = new ChkElement(this,
					element, _xElement.createAnyDefElement(), true);
			} else {//moreElements || ignoreOther
				_nextDefIndex = nextDefIndex;
				_actDefIndex = actDefIndex;
				result = new ChkElement(this,
					element,
					_xElement.createAnyDefElement(),
					_ignoreAll || _xElement._moreElements == 'I');
			}
		}
		_chkChildNodes.add(result);
		return result;
	}

	/** Check absence of an element node in model. */
	final void chkElementAbsence(final int index,
		final XElement xelem,
		final Counter c) {
		if ( _nil) {
			return;
		}
		if (_counters[index] >= xelem.minOccurs()) {
			if (_counters[index] == 0 && xelem._onAbsence >= 0) {
				ChkElement chkElem = new ChkElement(this, null, xelem, true);
				if (_clearReports) {
					clearTemporaryReporter();
				}
				_scp.exec(xelem._onAbsence, chkElem);
			}
			return;
		}
		if (xelem._onAbsence >= 0) {
			//we call the onAbsence method for all required items.
			_actDefIndex = index;
			for (int j = _counters[index]; j < xelem.minOccurs(); j++) {
				_elemValue = _element;
				//create "Pseudo" ChkElement
				ChkElement chkElem = new ChkElement(this, null, xelem, true);
				chkElem.setXXType((byte) 'E');
				if (_clearReports) {
					clearTemporaryReporter();
				}
				_scp.exec(xelem._onAbsence, chkElem);
				if (chkElem._element != null) {
					//save original reports and set clear new reporter
					ArrayReporter arep = getTemporaryReporter();
					setTemporaryReporter(new ArrayReporter());
					chkElem._ignoreAll = false; //set real mode
					if (c == null) {
						_element.appendChild(chkElem._element);
					} else {
						Node n = _element.getChildNodes().item(c._itemIdex);
						if (n == null) {
							_element.appendChild(chkElem._element);
						} else {
							_element.insertBefore(chkElem._element, n);
						}
					}
					_chkChildNodes.add(chkElem);
					chkElem.checkElement();
					chkElem.addElement();
					setTemporaryReporter(arep);//restore reporter
				}
			}
			_actDefIndex = -1;
		} else {
			String name = xelem.getName();
			if (xelem._xon > 0 && X_VALUE.equals(name = xelem.getLocalName())) {
				String[] x = getPosInfo(xelem.getXDPosition(), null);
				int ndx = (x[0].lastIndexOf("['"));
				if (ndx >= 0) {
					int ndx1 = x[0].indexOf("']", ndx);
					if (ndx1 > 0) {
						name = x[0].substring(ndx + 2, ndx1);
					}
				}
			}
			putTemporaryReport(_counters[index] == 0
				//Required element '&{0}' is missing
				? Report.error(XDEF.XDEF539,
					name + getPosMod(xelem.getXDPosition(), null))
				//Minimum occurrence not reached for &{0}
				: Report.error(XDEF.XDEF555,
					name + getPosMod(xelem.getXDPosition(),
						_xPos + "/" + name)));
		}
		copyTemporaryReports();
	}

	private String getTextPathIndex(final int index) {
		int counter = 0;
		for (int i = 0; i < index; i++) {
			if (_defList[i].getKind() == XMTEXT) {
				counter++;
			}
		}
		return (counter > 0) ? "[" + (counter+1) + "]" : "";
	}

	private Node getNodeByIndex(final int index) {
		Node n = _element.getFirstChild();
		for (int i = 0; n != null; n = n.getNextSibling()) {
			if (i++ == index) {
				return n;
			}
		}
		return null;
	}

	/** Check absence of a text node in model. */
	final void chkTextAbsence(final int index,
		final XData xtxt,
		final boolean ignoreAbsence,
		final Counter c) {
		if (_counters[index] != 0 || xtxt.minOccurs() <= XOccurrence.IGNORE) {
			return; //exists or IGNORED
		}
		_xdata = xtxt;
		String orig = _data = null;
		_parseResult = null;
		String xPos = _xPos;
		String txtname = getTextPathIndex(index); // index or ""
		_xPos += "/text()" + txtname;
		txtname = "$text" + txtname;
		if (!_attNames.contains(txtname) && xtxt._onAbsence >= 0) {
			_attNames.add(txtname);
			_elemValue = _element;
			if (_clearReports) {
				clearTemporaryReporter();
			}
			exec(xtxt._onAbsence, (byte) 'T'); //exec onAbsence
			if (_data != null) {
				checkDatatype(xtxt, true);
			}
			copyTemporaryReports();
		} else if (!ignoreAbsence && _data == null
			&& xtxt.minOccurs() >= XData.REQUIRED && !_nil) {
			error(XDEF.XDEF527); //Missing required text
		}
		if (_data == null && xtxt._deflt >= 0) {//exec default
			_data = null;
			_parseResult = null;
			_elemValue = _element;
			XDValue value = exec(xtxt._deflt, (byte) 'T');
			if (value != null) {
				_data = value.toString();
				checkDatatype(xtxt, true);
			}
			copyTemporaryReports();
		}
		debugXPos(XDDebug.FINALLY);
		if (xtxt._finaly >= 0) {
			_elemValue = _element;
			exec(xtxt._finaly, (byte) 'T');
			copyTemporaryReports();
		}
		if (_data != null) {
			if (!_data.equals(orig)) {
				Node txt = xtxt._cdata == 'T'
					? _rootChkDocument._doc.createCDATASection(_data)
					: _rootChkDocument._doc.createTextNode(_data);
				if (orig == null) {
					if (c == null) {
						_element.appendChild(txt);
					} else {
						_element.insertBefore(txt, getNodeByIndex(c._itemIdex));
						c._itemIdex++;
					}
					incRefNum();
				} else {
					_element.replaceChild(txt, getNodeByIndex(c._itemIdex));
				}
			}
		} else if (orig != null) {
			_element.removeChild(getNodeByIndex(c._itemIdex));
			c._itemIdex--;
			decRefNum();
			_data = null;
			_parseResult = null;
		}
		_xPos = xPos;
		_xdata = null;
		_parseResult = null;
	}

	private boolean isEmptyGroup(final int begIndex, final int endIndex) {
		for (int i = begIndex; i <= endIndex; i++) {
			switch (_defList[i].getKind()) {
				case XMSEQUENCE:
				case XMCHOICE:
				case XMMIXED:
				case XMSELECTOR_END:
					continue;
				default:
					if (_counters[i] != 0) {
						return false;
					}
			}
		}
		return true;
	}

	/** Check occurrences in a selector of sequence.
	 * @param selector Selector of group to be investigated.
	 * @param c Counter (index) of first item of the group.
	 * @param skipSelectors if true the internal selectors are skipped.
	 * @return true if nonempty content is required.
	 */
	private boolean checkSequenceAbsence(final SelectorState selector,
		final Counter c,
		final boolean skipSelectors) {
		boolean required = selector.minOccurs() > 0;
		if (selector._selective || !required && !selector._occur) {
			return false;
		}
		int endIndex = selector._endIndex;
		for (int i = selector._begIndex + 1; i < endIndex; i++) {
			XNode xnode;
			switch ((xnode = _defList[i]).getKind()) {
				case XMTEXT: {
					chkTextAbsence(i, (XData) xnode, false, c);
					required &= ((XData) xnode).minOccurs() > 0;
					if (c != null) {
						c._itemIdex += _counters[i];
					}
					continue;
				}
				case XMELEMENT: {
					chkElementAbsence(i, (XElement) xnode, c);
					required &= ((XElement) xnode).minOccurs() > 0;
					if (c != null) {
						c._itemIdex += _counters[i];
					}
					continue;
				}
				case XMSEQUENCE:
				case XMCHOICE:
					required &= ((XSelector) xnode).minOccurs() > 0;
					if (skipSelectors) {
						int j = ((XSelector) xnode)._endIndex;
						while (++i < j) {
							switch ((_defList[i]).getKind()) {
								case XMCHOICE:
								case XMMIXED:
								case XMSEQUENCE:
								case XMSELECTOR_END:
									continue;
								default:
									if (c != null) {
										c._itemIdex += _counters[i];
									}
							}
						}
					} else {
						SelectorState s =
							new SelectorState(selector, (XSelector) xnode);
						required =
							s._kind == XMCHOICE && s.minOccurs() > 0;
						required &= checkAbsence(s, c, skipSelectors);
						i = s._endIndex;
					}
					continue;
				case XMMIXED:
					if (skipSelectors) {
						int j = ((XSelector) xnode)._endIndex;
						while (++i < j) {
							switch ((_defList[i]).getKind()) {
								case XMCHOICE:
								case XMMIXED:
								case XMSEQUENCE:
								case XMSELECTOR_END:
									continue;
								default:
									if (c != null) {
										c._itemIdex += _counters[i];
									}
							}
						}
					} else {
						SelectorState s =
							new SelectorState(selector, (XSelector) xnode);
						required = (s._kind==XMCHOICE) && s.minOccurs()>0;
						required &= checkAbsence(s, c, skipSelectors);
						i = s._endIndex;
					}
					continue;
				case XMSELECTOR_END:
					if (skipSelectors) {
						continue;
					}
					return required;
				default:
			}
		}
		return required;
	}

	/** Check occurrences in a selector of mixed.
	 * @param selector Selector of group to be investigated.
	 * @param c Counter (index) of first item of the group.
	 * @param skipSelectors if true the internal selectors are skipped.
	 * @return true if nonempty content is required.
	 */
	private boolean checkMixedAbsence(final SelectorState selector,
		final Counter c,
		final boolean skipSelectors) {
		int endIndex = selector._endIndex;
		int begIndex = selector._begIndex + 1;
		boolean required = selector.minOccurs() > 0;
		boolean empty = isEmptyGroup(begIndex, endIndex);
		if (empty) {
			XSelector xs = (XSelector) _defList[selector._begIndex];
			if (xs._onAbsence >= 0) {
				if (skipSelectors || !required) {
					if (_clearReports) {
						clearTemporaryReporter();
					}
					exec(xs._onAbsence, (byte) 'U');
				}
				return false;
			}
			if (!required) {
				return false;
			}
		}
		if (!required && selector._selective) {
			return false;
		}
		if (!empty) {
			for (int i = begIndex; i < endIndex; i++) {
				XNode xnode;
				switch ((xnode = _defList[i]).getKind()) {
					case XMTEXT: {
						chkTextAbsence(i, (XData) xnode, false, c);
						required &= ((XData) xnode).minOccurs() > 0;
						if (c != null) {
							c._itemIdex += _counters[i];
						}
						continue;
					}
					case XMELEMENT: {
						chkElementAbsence(i, (XElement) xnode, c);
						required &= ((XElement) xnode).minOccurs() > 0;
						if (c != null) {
							c._itemIdex += _counters[i];
						}
						continue;
					}
					case XMCHOICE: {
						XChoice xch = (XChoice) xnode;
						required &= xch.minOccurs() > 0;
						int j = xch._endIndex;
						int k = 0; // number of occurrences
						for (int n = xch._begIndex; n <= j; n++) {
							k += _counters[n];
						}
						if (k < xch.minOccurs()) {
							//Minimum occurrence not reached for &{0}
							putTemporaryReport(
								Report.error(XDEF.XDEF555, "choice"));
							if (xch._onAbsence >= 0) {
								if (_clearReports) {
									clearTemporaryReporter();
								}
								exec(xch._onAbsence, (byte)'U');
							}
						}
						i = j;
						continue;
					}
					case XMSEQUENCE:
						required &= ((XSelector) xnode).minOccurs() > 0;
						if (skipSelectors) {
							int j = ((XSelector) xnode)._endIndex;
							while (++i < j) {
								switch ((_defList[i]).getKind()) {
									case XMCHOICE:
									case XMMIXED:
									case XMSEQUENCE:
									case XMSELECTOR_END:
										continue;
									default:
										if (c != null) {
											c._itemIdex += _counters[i];
										}
								}
							}
						} else {
							SelectorState s =
								new SelectorState(selector, (XSelector) xnode);
							required =
								s._kind == XMCHOICE && s.minOccurs() > 0;
							required &= checkAbsence(s, c, skipSelectors);
							i = s._endIndex;
						}
						continue;
					case XMMIXED:
						if (skipSelectors) {
							int j = ((XSelector) xnode)._endIndex;
							while (++i < j) {
								switch ((_defList[i]).getKind()) {
									case XMCHOICE:
									case XMMIXED:
									case XMSEQUENCE:
									case XMSELECTOR_END:
										continue;
									default:
										if (c != null) {
											c._itemIdex += _counters[i];
										}
								}
							}
						} else {
							SelectorState s =
								new SelectorState(selector, (XSelector) xnode);
							required =
								(s._kind==XMCHOICE) && s.minOccurs() > 0;
							required &= checkAbsence(s, c, skipSelectors);
							i = s._endIndex;
						}
						continue;
					case XMSELECTOR_END:
						if (skipSelectors) {
							continue;
						}
						return required;
					default:
				}
			}
		}
		if (required &&	!selector._occur &&	selector._count == 0) {
			if (!empty && selector._prev != null
				&& selector._prev._count >= selector._prev.minOccurs()) {
				return required;
			}
			if (_xElement._xon > 0) {
				//Missing required item(s) in &{0}
				error(XDEF.XDEF541, _xElement.getLocalName());
			} else {
				error(XDEF.XDEF520, //Sequence "xd:mixed" has no required item
					getPosMod(_defList[selector._begIndex].getXDPosition(),
						_xPos));
			}
		}
		return required;
	}

	/** Check occurrences in a selector of choice.
	 * @param selector Selector of group to be investigated.
	 * @param c Counter (index) of first item of the group.
	 * @param skipSelectors if true the internal selectors are skipped.
	 * @return true if nonempty content is required.
	 */
	private boolean checkChoiceAbsence(final SelectorState selector,
		final Counter c,
		final boolean skipSelectors) {
		if (selector.minOccurs() <= 0) {
			return false; // not required
		}
		boolean required = selector.minOccurs() > 0;
		int endIndex = selector._endIndex;
		for (int i = selector._begIndex + 1; i < endIndex; i++) {
			XNode xnode;
			switch ((xnode = _defList[i]).getKind()) {
				case XMTEXT: {
					XData xtxt = (XData) xnode;
					if (_counters[i] == 0) {
						if (xtxt.minOccurs() == 0) {
							return false; //optional variant
						}
						continue;
					}
					chkTextAbsence(i, xtxt, false, c);
					return false;
				}
				case XMELEMENT: {
					XElement xelem = (XElement) xnode;
					if (_counters[i] == 0) {
						if (xelem.minOccurs() == 0) {
							return false; //optional variant
						}
						continue;
					}
					chkElementAbsence(i, xelem, c);
					return false;
				}
				case XMSEQUENCE:
				case XMCHOICE:
					required &= ((XSelector) xnode).minOccurs() > 0;
					if (skipSelectors) {
						int j = ((XSelector) xnode)._endIndex;
						while (++i < j) {
							switch ((_defList[i]).getKind()) {
								case XMCHOICE:
								case XMMIXED:
								case XMSEQUENCE:
								case XMSELECTOR_END:
									continue;
								default:
									if (c != null) {
										c._itemIdex += _counters[i];
									}
							}
						}
					} else {
						SelectorState s =
							new SelectorState(selector, (XSelector) xnode);
						required =
							s._kind == XMCHOICE && s.minOccurs() > 0;
						required &= checkAbsence(s, c, skipSelectors);
						i = s._endIndex;
					}
					continue;
				case XMMIXED:
					if (skipSelectors) {
						int j = ((XSelector) xnode)._endIndex;
						while (++i < j) {
							switch ((_defList[i]).getKind()) {
								case XMCHOICE:
								case XMMIXED:
								case XMSEQUENCE:
								case XMSELECTOR_END:
									continue;
								default:
									if (c != null) {
										c._itemIdex += _counters[i];
									}
							}
						}
					} else {
						SelectorState s =
							new SelectorState(selector, (XSelector) xnode);
						required =
							(s._kind == XMCHOICE) && s.minOccurs() > 0;
						required &= checkAbsence(s, c, skipSelectors);
						i = s._endIndex;
					}
					continue;
				case XMSELECTOR_END:
					if (skipSelectors) {
						continue;
					}
					return required;
				default:
			}
		}
		if (!skipSelectors && selector._occur == false
			&& selector._count == 0 && required) {
			// do not report error if onAbsence
			if (((XSelector) _defList[selector._begIndex])._onAbsence < 0) {
				//Missing required item(s0 in &{0}
				error(XDEF.XDEF541,getPosMod(getXDPosition()+"/#choice",_xPos));
			}
		}
		return required;
	}

	/** Check occurrences in a selector.
	 * @param selector Selector of group to be investigated.
	 * @param c Counter (index) of first item of the group.
	 * @param skipSelectors if true the internal selectors are skipped.
	 * @return true if nonempty content is required.
	 */
	final boolean checkAbsence(final SelectorState selector,
		final Counter c,
		final boolean skipSelectors) {
		switch (selector._kind) {
			case XMCHOICE:
				return checkChoiceAbsence(selector, c, skipSelectors);
			case XMMIXED:
				return checkMixedAbsence(selector, c, skipSelectors);
		}
		return checkSequenceAbsence(selector, c, skipSelectors);
	}

	/** Finish processing of a group. */
	final void finishGroup() {
		_actDefIndex = -1;
		int finaly = _selector._finallyCode;
		debugXPos(XDDebug.SELECTORCREATE);
		if (_selector._kind == XMMIXED) {
			if (!_selector._occur) {//no variant reached
				if (_selector._count == 0) {
					if (_selector.minOccurs() > 0) {
						if (_selector._prev == null
							|| _selector._prev._kind == XMSEQUENCE) {
							checkAbsence(_selector, null, true);
						}
					}
				} else {
					checkAbsence(_selector, null, true);
				}
				if (_selector._prev != null) {
					_selector._prev._occur |= _selector._count > 0;
				}
				_selector = _selector._prev;
				_nextDefIndex++;
				if (finaly >= 0) {
					exec(finaly, (byte)'U');
				}
				return;
			}
			if (_selector._occur) {
				_selector._occur = false;
				_counters[_nextDefIndex] = ++_selector._count;
				_nextDefIndex = _selector._begIndex + 1;
			} else {
				if (_selector._prev != null) {
					_selector._prev._occur |= _selector._count > 0;
				}
				_selector = _selector._prev;
				_nextDefIndex++;
			}
			return;
		}
		if (_selector._kind == XMCHOICE) {
			if (_selector._occur) {
				_selector._count = ++_counters[_nextDefIndex];
			}
			if (_selector.maxOccurs() <= 1 || !_selector._occur) {
				if (_selector._count < _selector.minOccurs()
					&& (_selector._prev == null
					|| _selector._prev.minOccurs() > _selector._prev._count
					&& _selector._prev._begIndex == _selector._begIndex + 1
					&& _selector._prev._endIndex == _selector._endIndex - 1)) {
					//Minimum occurrence not reached for &{0}
					putReport(Report.error(XDEF.XDEF555, "choice"));
					XSelector xsel =
						(XSelector) getDefElement(_selector._begIndex);
					if (xsel._onAbsence >= 0) {
						if (_clearReports) {
							clearTemporaryReporter();
						}
						exec(xsel._onAbsence, (byte)'U');
					}
				}
				_selector.updateCounters();
				if (_selector._prev != null) {
					_selector._prev._occur |= _selector._count > 0;
					_selector = _selector._prev;
				} else {
					_selector = null;
				}
				_nextDefIndex++;
				if (finaly >= 0) {
					exec(finaly, (byte)'U');
				}
			} else {
				checkAbsence(_selector,
					new Counter(_selector._firstChild), true);
				if (_selector.maxOccurs() <= 1) {
					if (finaly >= 0) {
						exec(finaly, (byte) 'U');
					}
					_nextDefIndex++;
					return;
				}
				// Choice was not finished, will continue
				if (_selector.saveAndClearCounters()) {
					_selector._count++;
					_selector._occur = false;
				}
				_nextDefIndex = _selector._begIndex + 1;
			}
		} else {// _selector._kind == XNode.X_SEQUENCE
			if (_selector._occur) {
				checkAbsence(_selector,new Counter(_selector._firstChild),true);
				if (_selector._count >= _selector.maxOccurs() -1) {
					_selector._count++;
					if (_selector.minOccurs() >  _selector._count) {
						 //Maximum occurrence limit of &{0} exceeded
						error(XDEF.XDEF558, "sequence");
					}
					_selector.updateCounters(); //maximum reached
					_nextDefIndex++;
					if (_selector._prev != null) {
						_selector._prev._occur = true;
					}
					_selector = _selector._prev;
					if (finaly >= 0) {
						exec(finaly, (byte)'U');
					}
					return;
				}
			}
			if (_selector.maxOccurs() <= 1 || !_selector._occur) {
				if (_selector._prev == null) {
					if (!_selector._ignorable
						&& _selector.minOccurs() > 0) {
						if (!_selector._occur) {//was not checked
							checkAbsence(_selector,
								new Counter(_selector._firstChild), true);
						}
					}
					if (finaly >= 0) {
						exec(finaly, (byte) 'U');
					}
					_selector = null;
					_nextDefIndex++;
				} else {
					_selector._prev._occur |=
						_selector._occur || _selector._count > 0;
					if (_selector._prev._kind != XMSEQUENCE
						&& _selector._count > 0) {
						_selector._prev._occur = true;
						_nextDefIndex = _selector._prev._begIndex + 1;
					} else {
						if (finaly >= 0) {
							exec(finaly, (byte) 'U');
						}
						_nextDefIndex++;
					}
					_selector.updateCounters();
					_selector = _selector._prev;
				}
				if (finaly >= 0) {
					exec(finaly, (byte)'U');
				}
			} else {
				checkAbsence(_selector,new Counter(_selector._firstChild),true);
				if (_selector.saveAndClearCounters()) {
					_selector._count++;
					_selector._occur = false;
				}
				_nextDefIndex = _selector._begIndex + 1;
				if (finaly >= 0) {
					exec(finaly, (byte)'U');
				}
			}
		}
	}

	final boolean createGroup(final XSelector xs) {
		_actDefIndex = -1;
		if (xs.minOccurs() < 0) { //ignore, illegal
			_nextDefIndex = xs._endIndex + 1;
			return false;
		}
		if (xs._match >= 0 && !getXDDocument().isCreateMode()) {
			_elemValue = _element;
			XDValue value = exec(xs._match, (byte) 'U');
			if (xs._match >= 0) {
				if (value != null && !value.isNull()
					&& !value.booleanValue()) {
					_nextDefIndex = xs._endIndex + 1;
					return false; // not match
				}
			}
		}
		if (_selector == null || _selector._begIndex != _nextDefIndex) {
			_selector =	new SelectorState(_selector, xs);
			debugXPos(XDDebug.SELECTORINIT);
			if (xs._init >= 0) {
				exec(xs._init, (byte) 'U');
			}
		}
		_nextDefIndex++;
		if (xs.getKind() == XMCHOICE) {
			if (_counters[_selector._endIndex] >= _selector.maxOccurs()) {
				_nextDefIndex = _selector._endIndex + 1;
			}
		}
		return true;
	}

	/** Checks if all items of a mixed group are filled. If yes, call the method
	 * finishGroup() and _nextDefIndex is set after the mixed group.
	 * If the mixed group is the last one in a sequence group, then then the
	 * counter of occurrences of the sequence is increased.
	 */
	private void checkMixedAll() {
		if (_selector._prev != null
			&& _defList[_selector._prev._begIndex].maxOccurs() > 0
			&& _selector._kind == XMMIXED) {
			for (int i = _selector._begIndex + 1;
				i < _selector._endIndex; i++) {
				if (_counters[i] < _defList[i].maxOccurs()) {
					return;
				}
			}
			// all items of "mixed" sequence were processed; finish the group.
			_selector._occur = true;
			_selector._count++;
			_nextDefIndex = _selector._endIndex + 1;
			if (_selector._prev._kind != XMSEQUENCE
				|| _defList[_nextDefIndex].getKind()!=XMSELECTOR_END) {
				finishGroup();
				return;
			}
			if (_selector._prev.maxOccurs() == 1) {
				return;
			}
			if (_selector._prev._count <= _selector._prev.maxOccurs()) {
				_nextDefIndex = _selector._prev._begIndex + 1;
				_selector._prev._count++;
				_selector._prev._occur = true;
				finishGroup();
			} else if(_selector._prev._count > _selector._prev.maxOccurs()) {
				_nextDefIndex = _selector._prev._endIndex;
				 //Maximum occurrence limit of &{0} exceeded
				error(XDEF.XDEF558, "sequence");
			}
		}
	}

	/** Search XNode in the list of nodes.

* This is very tricky method. It is necessary to know that it is invoked * when a child node occurs. There are important variables:

* _defList .. array of XNodes
* _counters .. array of occurrence counters
* _actDefIndex .. -1 or index to actually processed XNode item
* _nextDefIndex .. index to the XNode item to be inspected.
* _selector .. container of information about actual selection block.
* @param el the element or null it text node is processed. * @return XNode or ChkElement object or null. */ private Object findXNode(final Element el) { XNode xn; ChkElement result; int defLength = _defList.length; int lastNextDefIndex = _nextDefIndex; int lastActDefIndex = _actDefIndex; if (el == null) { // element is null => text node if (_nextDefIndex < defLength && _defList[_nextDefIndex].getKind() != XMTEXT) { if ((xn = _xElement.getDefAttr("$text", -1)) != null) { return xn; } else if ((xn=_xElement.getDefAttr("$textcontent",-1))!=null) { return new XData( //dummy, just process string() "$text", null, xn.getXDPool(), XMTEXT); } } } else if (_actDefIndex >= 0 && (xn = _defList[_actDefIndex]).getKind() == XMELEMENT && xn.maxOccurs() > 1 && (_selector == null || _selector._kind != XMMIXED)) { if ((result = chkElem((XElement) xn, el)) != null) { // repeated nodes if (xn.maxOccurs() != 1 || xn.minOccurs() != 1) {//???template if (_counters[_actDefIndex] < xn.maxOccurs() || _actDefIndex+1 >= defLength) { // max occurrence not reached or in the X-definition // not follows a XElement node return result; } // maxOccurrence exceeded, so check if the next node in // is an element with the same name. XNode x = _defList[_actDefIndex+1]; if (x.getKind() != XMELEMENT || !xn.getName().equals(x.getName())) { return result; // not XElement node or not same name } // so we force to skip to the next model of element _nextDefIndex = _actDefIndex + 1; } } if (xn.minOccurs() > _counters[_actDefIndex]) { chkElementAbsence(_actDefIndex, (XElement) xn, null); } } if (_selector!= null) { if (_selector._kind == XMMIXED) { _nextDefIndex = _selector._begIndex + 1; } } _actDefIndex = -1; while (_nextDefIndex < defLength) { short kind; switch (kind = (xn = _defList[_nextDefIndex]).getKind()) { case XMTEXT: { if (el == null) {// is text node int oldDefIndex = _actDefIndex; _actDefIndex = _nextDefIndex++; XData xd = (XData) xn; if (xd._match >= 0 && !getXDDocument().isCreateMode()) { _elemValue = _element; XDValue item = exec(xd._match, (byte) 'T'); copyTemporaryReports(); if (item == null || !item.booleanValue()) { _actDefIndex = oldDefIndex; continue; } } if (_selector != null) { _selector._occur = true; //found if (_selector._kind == XMCHOICE) { _nextDefIndex = _selector._endIndex; } else { checkMixedAll(); } } return xd; } if (_selector == null) { genAbsentText((XData) xn); } else { _nextDefIndex++; } _actDefIndex = -1; continue; } case XMELEMENT: { XElement xel = (XElement) xn; if (el != null) { // not text node (element) int oldDefIndex = _actDefIndex; _actDefIndex = _nextDefIndex; // save actual index if ((result = chkElem(xel, el)) != null) { _nextDefIndex++; if (_selector != null) { _selector._occur = true; if (_selector._kind == XMCHOICE) { _nextDefIndex = _selector._endIndex; if (_selector._count>_selector.maxOccurs()){ //Maximum occurrence limit // of &{0} exceeded error(XDEF.XDEF558, "choice"); } } else { checkMixedAll(); } } return result; } _actDefIndex = oldDefIndex; // reset actual index } else if (xel.minOccurs() <= 0) { // el == null => text node and this element not required _nextDefIndex++; // next item continue; } if (_selector == null || _selector._kind == XMSEQUENCE && !_selector._selective && _selector._prev == null) { int index = _nextDefIndex; int counter = _counters[index]; if (counter == 0 && xel._onAbsence >= 0 && xel.minOccurs() == 0) { // not required element, however execute onAbsence chkElementAbsence(index, xel, null); _nextDefIndex++; continue; } if (counter < xel.minOccurs()) { //required element is missing while (_nextDefIndex + 1 < _defList.length) { XNode x = _defList[_nextDefIndex + 1]; if (x.getKind()==XMELEMENT && el!=null) { if ((result=chkElem((XElement)x,el))!=null){ chkElementAbsence(index, xel, null); //following element is OK _nextDefIndex++; if (_selector != null) { _selector._occur = true; //found } _actDefIndex = _nextDefIndex++; return result; } else if (x.minOccurs() <= 0) { // skip next XMELEMENT if not required _actDefIndex = _nextDefIndex++; continue; } } break; } if (_selector == null) { return null; //not in sequence, not found } } } else if (_selector._kind == XMSEQUENCE && _selector._selective && _nextDefIndex == _selector._begIndex + 1) { _nextDefIndex = _selector._endIndex; continue; } _nextDefIndex++; continue; } case XMSELECTOR_END: { if (_selector == null) {//??? _nextDefIndex++; return null; } if (el == null && _selector._prev == null && _selector._kind == XMMIXED && (_nextDefIndex == defLength -1 || _defList[_nextDefIndex+1].getKind()!=XMTEXT)) { //just to improve error reporting ??? _nextDefIndex = lastNextDefIndex; _actDefIndex = lastActDefIndex; return null; } finishGroup(); continue; } case XMSEQUENCE: case XMMIXED: case XMCHOICE: { createGroup((XSelector) xn); continue; } default: //error - unknown kind //Internal error&{0}{: } throw new SRuntimeException(SYS.SYS066, "Xdefinifion - ChkElement, unknown item: "+kind+" "+xn); } } return null; } /** Generate missing text if it is required. */ private void genAbsentText(final XData xtxt) { _actDefIndex = _nextDefIndex++; _xdata = xtxt; String xPos = _xPos; _xPos += "/text()"; int ndx = -1; for (Node n=_element.getFirstChild(); n!=null; n=n.getNextSibling()) { if (n.getNodeType() == Node.TEXT_NODE) { ndx++; } } if (ndx >= 0) { _xPos += "[" + (ndx + 2) + "]"; } if (xtxt._deflt >= 0) {//exec default _elemValue = _element; XDValue value = exec(xtxt._deflt, (byte) 'T'); if (value != null) { _data = value.toString(); checkDatatype(xtxt, true); } copyTemporaryReports(); } if (_data == null && xtxt.minOccurs() == XData.REQUIRED) { //required && missing obligatory text if (xtxt._onAbsence >= 0) { if (_clearReports) { clearTemporaryReporter(); } //exec onAbsence _elemValue = _element; exec(xtxt._onAbsence, (byte) 'T'); if (_data != null) { checkDatatype(xtxt, true); } copyTemporaryReports(); } if (_data == null) { if (_selector == null || !_selector._error) { if (xtxt._onAbsence < 0 && !_nil) { error(XDEF.XDEF527); //Missing required text in } if (_selector != null) { _selector._error = true; //don't repeat error reporting } } } } if (_data != null) { appendTextNode(_data, xtxt); incRefNum(); } _xPos = xPos; _xdata = null; _parseResult = null; } private Node appendTextNode(final String data, final XData xtxt) { Node txt = xtxt._cdata == 'T' ? _rootChkDocument._doc.createCDATASection(data) : _rootChkDocument._doc.createTextNode(data); _element.appendChild(txt); if (_scp.getXmlStreamWriter() != null) { try { _scp.getXmlStreamWriter().writeNode(txt); } catch (SRuntimeException ex) { putReport(ex.getReport()); } } return txt; } /** Check if element complies with model. * @param xel the XElement object. * @param el The source element from which the result is composed. * @return ChkElement object or null if element do not comply. */ private ChkElement chkElem(final XElement xel, final Element el) { if (!"$any".equals(xel.getName())) { String localName = xel.getLocalName(); String s = _rootChkDocument.findInLexicon(xel.getXDPosition()); if (s != null) { localName = s; } s = el.getNodeName(); int ndx = s.indexOf(':'); if (ndx >= 0) { s = s.substring(ndx + 1); } if (!localName.equals(s)) { return null; } String uri = el.getNamespaceURI(); if (uri == null) { if (xel.getNSUri() != null) { return null; } } else if (!uri.equals(xel.getNSUri())) { return null; } } ChkElement chkEl = new ChkElement(this, el, xel, false); if (chkEl._nil) { return chkEl; } if (xel._match >= 0 && !getXDDocument().isCreateMode()) { //Execute "match" action on element chkEl._elemValue = chkEl._element; chkEl.setXXType((byte) 'E'); XDValue result = _scp.exec(xel._match, chkEl); copyTemporaryReports(); if(result == null || !result.booleanValue()) { String s = _xPos + '/' + chkEl._name; XPosInfo x = _xPosOccur.get(s); if (x != null) { // never should be null! if (x.subCount() <= 0) { // decrease xpath counter _xPosOccur.remove(s); // if it is the first remove it } } return null; } } chkEl.initElem(); return chkEl; } /** Prepare variables and execute the init section of X-script. */ final void initElem() { // prepare variables declared in the script (do not make it twice) if (_xElement._varinit >= 0 && _variables == null) { _variables = new XDValue[_xElement._varsize]; exec(_xElement._varinit, (byte) 'E'); copyTemporaryReports(); } debugXPos(XDDebug.INIT); if (_xElement._init >= 0) { _elemValue = _element; exec(_xElement._init, (byte) 'E'); copyTemporaryReports(); _elemValue = null; } } /** Get actual X-definition assigned to node. * @param index The index of X-definition. * @return The actual definition or null. */ public final XNode getDefElement(final int index) { return index < _defList.length ? _defList[index] : null; } /** Get maximal index of X-definition in the list. * @return Max index of definition list. */ final int getDefinitionMaxIndex() {return _defList.length;} /** Add the new attribute to the current element. * @param att The object with attribute. * @return true if attribute was created according to X-definition. */ final boolean newAttribute(final Attr att) { _node = att; boolean result = addAttributeNS(att.getNamespaceURI(), att.getName(),att.getValue()); _node = null; return result; } /** Process attribute white spaces. * @param xatt Model of attribute. * @param data value of attribute. * @return modified value. */ private String attrWhitespaces(final XData xatt, final String data) { String result; if ((xatt != null && xatt._attrWhiteSpaces!= 0) ? xatt._attrWhiteSpaces == 'T' : (_xElement._attrWhiteSpaces!= 0) ? _xElement._attrWhiteSpaces == 'T' : _rootChkDocument._attrWhiteSpaces == 'T') { result = SUtils.trimAndRemoveMultipleWhiteSpaces(data); } else if ((xatt != null && xatt._trimAttr != 0) ? xatt._trimAttr != 'F' : (_xElement._trimAttr != 0) ? _xElement._trimAttr != 'F' : _rootChkDocument._trimAttr != 'F') { result = data.trim(); } else { result = data; } if (result.isEmpty()) { if ((xatt != null && xatt._ignoreEmptyAttributes != 0) ? xatt._ignoreEmptyAttributes == 'T' : (_xElement._ignoreEmptyAttributes != 0) ? _xElement._ignoreEmptyAttributes == 'T' : _rootChkDocument._ignoreEmptyAttributes == 'T') { return null; } return result; } if (xatt != null && xatt.isFixed()) { return result; } byte c = (xatt != null && xatt._attrValuesCase != 0) ? xatt._attrValuesCase : _xElement._attrValuesCase != 0 ? _xElement._attrValuesCase : _rootChkDocument._setAttrValuesCase; return c == 'T' ? result.toUpperCase() : c == 'F' ? result.toLowerCase() : result; } private XData getXAttr(final String nsURI, final String qname) { if (nsURI == null) { return getXAttr(qname); } int ndx = qname.indexOf(':'); String localName = qname.substring(ndx + 1); XData xatt = _xElement.getDefAttrNS(nsURI, localName, _rootChkDocument._sourceLanguageID); if (xatt == null && nsURI.equals(_element.getNamespaceURI())) { XData xa = _xElement.getDefAttr( localName, _rootChkDocument._sourceLanguageID); if (xa != null && xa._acceptQualifiedAttr == 'T') { return xa; } } return xatt; } private XData getXAttr(final String name) { return _xElement.getDefAttr(name, _rootChkDocument._sourceLanguageID); } /** Execute validation method and if putTempErrors is true then put errors * to reporter. * @param xdata model of data. * @param putTempErrors if true then put errors to reporter. */ private void checkDatatype(final XData xdata, final boolean putTempErrors) { if (xdata._check >= 0) { XDValue item = exec(xdata._check, (byte) 'A'); if (item.getItemId() == XD_PARSERESULT) { _parseResult = (XDParseResult) item; _data = _parseResult.getSourceBuffer(); if (_xComponent != null && _parseResult.matches() && getXMNode()!=null && getXMNode().getXDPosition()!=null) { if ("$text".equals(xdata.getName())) { _xComponent.xSetText(this, _parseResult); } else { _xComponent.xSetAttr(this, _parseResult); } } } else { _parseResult = new DefParseResult(_data); if (item.booleanValue()) { if (_xComponent != null && getXMNode() != null && getXMNode().getXDPosition() != null) { _parseResult.setParsedValue(_data); _xComponent.xSetAttr(this, _parseResult); } } else { //XDEF515=Value error&{0}{ :} _parseResult.putDefaultParseError(); } } } else {//default: do not check; i.e. always true setXXType((byte) 'A'); _parseResult = new DefParseResult(_data); if (_xComponent != null && getXMNode() != null && getXMNode().getXDPosition() != null) { _parseResult.setParsedValue(_data); _xComponent.xSetAttr(this, _parseResult); } } if (!_parseResult.matches()) { //error for (Report rep: _parseResult.getReporter()) { String s = rep.getModification(); if (s == null) { s = ""; } rep.setModification(s + getPosMod(xdata.getXDPosition(),_xPos)); if (putTempErrors) { _scp.getTemporaryReporter().putReport(rep); } } if (!chkTemporaryErrors()) { if (putTempErrors) { //Value error putTemporaryReport(Report.error(XDEF.XDEF515)); } } } else if (_xElement._xon > 0) { Object value = _parseResult.getParsedValue(); if (value==null) { value = _parseResult.getSourceBuffer(); } if (X_KEYATTR.equals(xdata.getName())) { _xonKey = XonTools.xmlToJName(value.toString()); } else if (X_VALATTR.equals(xdata.getName())) { if (_xElement.getNSUri() == null ) { _xonKey = XonTools.xmlToJName(_element.getTagName()); } else if (_xonKey == null && _element.hasAttribute(X_KEYATTR)){ _xonKey = XonTools.xmlToJName(_element.getAttribute(X_KEYATTR)); } if (value instanceof XDValue) { XDValue x = (XDValue) value; if (x.isNull() || x.getItemId() == XD_NULL) { _xonValue = null; } else { Object obj = x.getObject(); if (obj instanceof Number) { switch (xdata.getParserName()) { case "byte": _xonValue = x.byteValue(); break; case "unsignedByte": case "short": _xonValue = x.shortValue(); break; case "unsignedShort": case "int": _xonValue = x.intValue(); break; case "float": _xonValue = x.floatValue(); break; default: _xonValue = obj; // "decimal", "dec" } } else if (obj instanceof String) { _xonValue = XonTools.xmlToJValue((String) obj); } else { _xonValue = obj; } } } else { _xonValue = value; } } } } /** Remove attribute from actual element. * @param nsURI namespace URI of attribute. * @param qname qualified name of attribute. */ private void removeAttr(final String nsURI, final String qname) { if (nsURI != null) { int ndx = qname.indexOf(':'); _element.removeAttributeNS(nsURI, ndx >= 0 ? qname.substring(ndx + 1) : qname); } else { _element.removeAttribute(qname); } } /** If data value was changed in a section then run validation method * @param xatt model of attribute. * @param oldData original value of dara. * @param nsURI namespace URI of attribute. * @param qname qualified name of attribute. */ private void updateAttrValue(final XData xatt, final String oldData, final String nsURI, final String qname) { copyTemporaryReports(); if (_data != oldData) { // _data was changed, even equal if (_data == null) { removeAttr(nsURI, qname); } else { checkDatatype(xatt, false); } } } //////////////////////////////////////////////////////////////////////////////// /** Get reference counter of given node (with an index). * @param index the index of inspected node. * @return The reference number. */ public final int getRefNum(final int index) {return _counters[index];} /** Set attribute to the current element. First remove the original * attribute if exists to prevent report about attribute redefinition. * @param name The name of attribute. * @param value The value of attribute. * @return true if attribute was created according to X-definition. */ public final boolean setAttribute(final String name, final String value) { _element.setAttribute(name, value); return newAttribute(_element.getAttributeNode(name)); } /** Set attribute to the current element. First remove the original * attribute if exists to prevent report about attribute redefinition. * @param name The name of attribute. * @param data The value of attribute. * @param nsURI The value of name space URI. * @return true if attribute was created according to X-definition. */ public final boolean setAttribute(final String name, final String data, final String nsURI) { if (nsURI == null) { return setAttribute(name, data); } _element.setAttributeNS(nsURI, name, data); return newAttribute(_element.getAttributeNode(name)); } /** Update actual element in the tree. If argument is null the actual * element will be removed from the tree. * @param el The element which will replace the actual one. */ public final void updateElement(final Element el) { Element el1, el2; if ((el2 = el) == null) { //remove child if (_element != null) { if (_parent._parent == null) { // root element try { ((ChkDocument) _parent). getDocument().removeChild(_element); } catch (DOMException ex) {} ((ChkDocument) _parent)._element = null; } else if ((el1 = _parent.getElement()) != null ) { el1.removeChild(_element); } _element = null; } } else if (el2 != _element) { //update child if (_element == null) { if (_parent._parent == null) { // root element ((ChkDocument) _parent).getDocument().appendChild(el2); } else { if ((el1 = _parent.getElement()) != null) { el1.appendChild(el2); } } } else { if (_parent._parent == null) { // root element ChkDocument root = ((ChkDocument) _parent); root.getDocument().replaceChild(el2, _element); root._element = el2; } else if ((el1 = _parent.getElement()) != null) { Document doc = _element.getOwnerDocument(); Document doc1 = el1.getOwnerDocument(); if (doc != doc1) { el1 = (Element) doc.importNode(el1, true); } if (el2 != doc1.getDocumentElement()) { Document doc2 = el2.getOwnerDocument(); if (doc != doc2) { el2 = (Element) doc.importNode(el2, true); } if (el2 != doc1.getDocumentElement()) { el1.replaceChild(el2, _element); } } } } _element = el2; } } final void finishSelector() { if (_selector == null) { return; } //2. ckeck if there is list of pending selector if (!_selector._ignorable && _selector.minOccurs() > 0 && _selector.minOccurs() > (_selector._occur ? _selector._kind == XMMIXED ? 1 : ++_selector._count : _selector._count)) { XSelector xsel = (XSelector) getDefElement(_selector._begIndex); //Minimum occurrence not reached for &{0} error(XDEF.XDEF555, xsel.getName().substring(1)); debugXPos(XDDebug.ONABSENCE); if (xsel._onAbsence>= 0) { exec(xsel._onAbsence, (byte)'U'); } } boolean nested = false; while (_selector != null) { if (_selector._kind == XMCHOICE) { nested = true; if (_selector._occur && _nextDefIndex == _selector._endIndex) { _selector._count = ++_counters[_nextDefIndex]; } } else if (_selector._occur) { _selector._count++; } //check absence within a group. If actual node is the end of a group // then set "skipselector" to true, othewise to false. checkAbsence(_selector, null, _nextDefIndex < _defList.length && _defList[_nextDefIndex].getKind() == XMSELECTOR_END && nested); if (_selector._kind == XMSEQUENCE && _selector._count <_selector.minOccurs()) { //Minimum occurrence not reached for &{0} error(XDEF.XDEF555, "sequence"); } nested = true; _actDefIndex = -1; _nextDefIndex = _selector._endIndex + 1; debugXPos(XDDebug.SELECTORFINALLY); if (_selector._finallyCode >= 0) { exec(_selector._finallyCode, (byte) 'U'); } if (_selector._prev != null) { _selector._prev._count++; } _selector = _selector._prev; } } /** Finish checking of model. * @param element the element or null it text node is processed. * @return the XNode object from the list or null. */ private void finishModel() { _parseResult = null; //1. check if last element occurrence if (_actDefIndex >= 0) { // check last processed item XNode xn = _defList[_actDefIndex]; if (xn.getKind() == XMELEMENT) { if (xn.minOccurs() > _counters[_actDefIndex]) { chkElementAbsence(_actDefIndex, (XElement) xn, null); } } else if (xn.getKind() == XMTEXT) { if (xn.minOccurs() > _counters[_actDefIndex]) { chkTextAbsence(_actDefIndex, (XData) xn, false, null); } } _actDefIndex = -1; } finishSelector(); //3. check remaining part of model. int nextDefIndex = _nextDefIndex; while (_nextDefIndex < _defList.length) { short kind; XNode xnode; switch (kind = (xnode = _defList[_nextDefIndex]).getKind()) { case XMTEXT: case XMELEMENT: { if (_selector != null && _selector._selective) { if ((_selector.maxOccurs() <= 1 || !_selector._occur) && _selector._count <= _selector.minOccurs()) { if (_selector._prev == null || _selector._prev._count+1 <= _selector._prev.minOccurs()) { XSelector xsel = (XSelector) getDefElement(_selector._begIndex); if (xsel._onAbsence>=0 && (_selector._count==0 || _selector._count<_selector.minOccurs())){ debugXPos(XDDebug.ONABSENCE); exec(xsel._onAbsence, (byte)'U'); } else if (_selector._count <_selector.minOccurs()){ String s = _selector._kind == XMCHOICE ? "choice" : _selector._kind==XMMIXED ? "mixed" : "sequence"; //Minimum occurrence not reached for &{0} error(XDEF.XDEF555, s); } } } _nextDefIndex = _selector._endIndex + 1; _selector = _selector._prev; break; } else { _nextDefIndex++; } continue; } case XMSELECTOR_END: { _actDefIndex = -1; if (_selector == null) { _nextDefIndex++; continue; } switch (_selector._kind) { case XMMIXED: { if (!_selector._occur) {// no variant reached if (_selector._count == 0) { if (_selector.minOccurs() > 0) {// not empty checkAbsence(_selector, null, true); } } else { checkAbsence(_selector, null, true); } if (_selector._prev == null) { _selector = null; } else { _selector._prev._occur|=_selector._count>0; _selector = _selector._prev; } _nextDefIndex++; continue; } if (_selector._occur) { _selector._occur = false; _selector._count = 1; _nextDefIndex = _selector._begIndex + 1; } else { if (_selector._prev == null) { _selector = null; } else { _selector._prev._occur|=_selector._count>0; _selector = _selector._prev; } _nextDefIndex++; } continue; } case XMCHOICE: { if (_selector._occur) { _selector._count = ++_counters[_selector._endIndex]; } if (_selector.maxOccurs()<=1 || !_selector._occur) { if (!_selector._ignorable && _selector._count < _selector.minOccurs()) { //Minimum occurrence not reached for &{0} error(XDEF.XDEF555, "choice"); XSelector xsel = (XSelector) getDefElement(_selector._begIndex); debugXPos(XDDebug.ONABSENCE); if (xsel._onAbsence >= 0) { exec(xsel._onAbsence, (byte)'U'); } } _selector.updateCounters(); _nextDefIndex++; if (_selector._prev == null) { _selector = null; } else { _selector._prev._occur|=_selector._count>0; _selector = _selector._prev; } } else { checkAbsence(_selector, new Counter(_selector._firstChild), true); if (_selector.maxOccurs() <= 1) { _nextDefIndex++; continue; } if (_selector.saveAndClearCounters()) { _selector._count++; _selector._occur = false; } _nextDefIndex = _selector._begIndex + 1; } continue; } default: { if (_selector.maxOccurs()<=1 || !_selector._occur) { if (_selector._prev == null) { _selector = null; _nextDefIndex++; } else { _selector._prev._occur |= _selector._occur || _selector._count>0; if (_selector._prev._kind!=XMSEQUENCE && _selector._count > 0) { _selector._prev._occur = true; _nextDefIndex = _selector._begIndex + 1; } else { _nextDefIndex++; } _selector.updateCounters(); _selector = _selector._prev; } } else { checkAbsence(_selector, new Counter(_selector._firstChild), true); if (_selector.saveAndClearCounters()) { _selector._count++; _selector._occur = false; } _nextDefIndex = _selector._begIndex + 1; } continue; } } } case XMSEQUENCE: case XMMIXED: case XMCHOICE: { _actDefIndex = -1; if (_selector != null && _selector._selective) { if ((!_selector._ignorable && _selector._count < _selector.minOccurs()) && (_selector._prev == null || _selector._prev.minOccurs() >_selector._prev._count && _selector._prev._begIndex==_selector._begIndex+1 && _selector._prev._endIndex==_selector._endIndex-1) && (_selector.maxOccurs()<=1 || !_selector._occur)){ //Minimum occurrence not reached for &{0} error(XDEF.XDEF555, xnode.getName().substring(1)); XSelector xsel = (XSelector) xnode; if (xsel._onAbsence >= 0) { exec(xsel._onAbsence, (byte)'U'); } } _nextDefIndex = _selector._endIndex + 1; _selector = _selector._prev; break; } if (_selector==null || _selector._begIndex!=_nextDefIndex) { _selector = new SelectorState(_selector, (XSelector) xnode); } _nextDefIndex++; continue; } default: //error - unknkown kind //Internal error&{0}{: } throw new SRuntimeException(SYS.SYS066, "kind: " + kind); } break; } //check absence of items in root selection if ((_nextDefIndex = nextDefIndex) < _defList.length) { XSequence xs = new XSequence(); xs.setOccurrence(1, 1); xs._begIndex = _nextDefIndex - 1; xs._endIndex = _defList.length; _selector = new SelectorState(null, xs); checkAbsence(_selector, new Counter(_element.getChildNodes().getLength()), false); _selector = null; } } /** Process text white spaces before processing. * @param xd XData model or null. * @param data String to be processed. * @return text with processed white spaces. */ final String textWhitespaces(final XCodeDescriptor xd, final String data){ String result; if ((xd != null && xd._textWhiteSpaces != 0) ? xd._textWhiteSpaces == 'T' : _xElement._textWhiteSpaces == 'T') { result = SUtils.trimAndRemoveMultipleWhiteSpaces(data); } else if ((xd != null && xd._trimText != 0) ? xd._trimText != 'F' : (_xElement._trimText != 0) ? _xElement._trimText != 'F' : _rootChkDocument._trimText != 'F') { result = data.trim(); } else { result = data; } if (result.isEmpty() || xd != null && xd.isFixed()) { return result; } byte b = (xd != null && xd._textValuesCase != 0) ? xd._textValuesCase : _xElement._textValuesCase != 0 ? _xElement._textValuesCase : _rootChkDocument._xElement._textValuesCase; return b == 'T' ? result.toUpperCase() : b == 'F' ? result.toLowerCase() : result; } /** Destruct ChkElement. */ private void closeChkElement() {//just let's gc do the job _scp.closeFinalList(getFinalList()); // close objects from final list for (CodeUniqueset x: _markedUniqueSets) { String s = x.checkNotMarked(this); if (!s.isEmpty()) { //Not referred keys found in the uniqueSet &{0}&{1}{: } error(XDEF.XDEF524, x.getName(), s); } } if (_scp.getXmlStreamWriter() != null) { //write the end of element if XML stream writer exists. try { _scp.getXmlStreamWriter().writeElementEnd(); } catch (SRuntimeException ex) { putReport(ex.getReport()); } } if (_xComponent != null) { if (_xComponent.xGetModelPosition().indexOf("/$any") > 0 || _xComponent.xGetModelPosition().endsWith("#*")) { if (!(_forget || _xElement._forget == 'T')) { // not forget _xComponent.xSetAny(_element); } } if (_xComponent.xGetParent() != null && _xComponent != getParent().getXComponent()) { if (!(_forget || _xElement._forget == 'T')) { // not forget _xComponent.xGetParent().xAddXChild(_xComponent); } } } if (!getXDDocument().isCreateMode() && (_forget || _xElement._forget == 'T' || _xComponent != null)) { // not create mode and forget or _xComponent != null updateElement(null); _parent.getChkChildNodes().remove(this); _chkChildNodes = null; _xElement = null; _element = null; _xonArray = null; _xonKey = null; _xonMap = null; _xonValue = null; } _xComponent = null; if (_variables != null) { for(int i = 0; i < _variables.length; i++) { XDValue x = _variables[i]; if (x != null && !x.isNull() && (x.getItemId() == X_UNIQUESET || x.getItemId() == X_UNIQUESET_M)) { CodeUniqueset y = (CodeUniqueset)x; y.checkAndClear(_scp.getTemporaryReporter()); } _variables[i] = null; } _variables = null; } _xPosOccur.clear(); _defList = new XNode[0]; _counters = new int[0]; _actDefIndex = -1; _xPos = null; _elemValue = null; _sourceElem = null; _data = null; _parseResult = null; _attNames = null; _selector = null; _userObject = null; _attName = null; _attURI = null; _xdata = null; if (_boundKeys != null) { for (XDUniqueSetKey x: _boundKeys) { if (x != null) { x.resetKey(); } } } } /*XX ?? */ /** Mark unique set with this instance of ChkElement. * @param us unique set. */ public final void addMarkedUniqueset(CodeUniqueset us) { _markedUniqueSets.add(us); us.setMarker(this); } /*XX ?? */ //////////////////////////////////////////////////////////////////////////////// // Methods to retrieve values from checked tree. //////////////////////////////////////////////////////////////////////////////// /** Look up for the X-Position (XPos) of the element set by xPath. * For look up is used the hash table with the XPaths and their * occurrences. * @param xPath the XPath to the current ChkElement (Element * from the source XML document that is actually processed). * @return the position of this element in the source XML document * to complete XPath identifier. */ private int getElemXPos(Map xPosOccur, String xPath) { if(xPosOccur != null) { // never should happen!! XPosInfo xPathInfo; if ((xPathInfo = xPosOccur.get(xPath)) == null) { // first occurrence of the xPath xPosOccur.put(xPath, new XPosInfo()); return 1; } else { // another (second and more) occurrence of the xPath return xPathInfo.addCount(); } } // Never should happen - internal error throw new SRuntimeException(XDEF.XDEF569, //Fatal error&{0}{: } "ChkElement:getElemXPos: _xPathOccur == null"); } /** Saved counter object.*/ private static final class Counter { int _itemIdex; Counter(final int counter) {_itemIdex = counter;} } /** Class to represent short information about XPaths for all elements * present in the input XML source. * This class is not deleted after element processing when "forget" * option is specified !!! * This class is deleted (nulled) when the end of parent element is reached * in the XML source. * Maximum recommended size of object created from this class is 8 kB * to avoid OutOfMemory exception by processing very large XML sources. */ private static final class XPosInfo { /** Field to count the amount of the same XPaths. */ private int _counter; /** Creates info object in case of first occurrence of this XPath * in the input XML source. */ XPosInfo() {_counter = 1;} /** Increase counter of occurrence of XPath . * @return increased occurrence of this XPath . */ int addCount() {return ++_counter;} /** Decrease counter of occurrence of XPath. * @return decreased occurrence of this XPath . */ int subCount() {return --_counter;} } //////////////////////////////////////////////////////////////////////////////// /** Add the new attribute to the current XXElement. * @param qname The qualified name of attribute (including prefix). * @param data The value of attribute. * @param nsURI The value of namespace URI. * @return true if attribute was created according to X-definition. */ @Override public final boolean addAttributeNS(final String nsURI, final String qname, final String data) { if (_element == null) { return _ignoreAll; } if ("xmlns".equals(qname) || qname.startsWith("xmlns:") || qname.startsWith("xml:")) { String uri = qname.startsWith("xml:") ? XMLConstants.XML_NS_URI : XMLConstants.XMLNS_ATTRIBUTE_NS_URI; _element.setAttributeNS(uri, qname, data); return true; } if (_ignoreAll) { return true; //all checks areignored (undef element) } if (XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI.equals(nsURI)) { return false; } if (_nil) {// XML schema; xsi:nil & options nillable error(XDEF.XDEF525, //Attribute not allowed qname, getPosMod(getXDPosition(), _xPos)); return false; } XData xatt = (nsURI != null) ? getXAttr(nsURI, qname) : getXAttr(qname); if (xatt == null) { xatt = _xElement.getDefAttr("$attr", -1); // any attr } String adata; if ((adata = attrWhitespaces(xatt, data)) == null) { removeAttr(nsURI, qname); return true; } String xPos = _xPos; _xPos += "/@" + qname; _attName = qname; _attURI = nsURI; _xdata = xatt; if (xatt!=null && xatt._match >=0 && !getXDDocument().isCreateMode()) { _elemValue = _element; _data = adata; XDValue item = exec(xatt._match, (byte) 'A'); _elemValue = null; _data = null; _parseResult = null; if (item != null && !item.booleanValue()) {//delete it removeAttr(nsURI, qname); if (xatt.minOccurs() != XOccurrence.IGNORE) { if (xatt.minOccurs() != XOccurrence.ILLEGAL) { //Attribute not allowed putTemporaryReport(Report.error(XDEF.XDEF525, qname, getPosMod(getXDPosition(), _xPos))); } if (xatt._onIllegalAttr >= 0) { if (_clearReports) { clearTemporaryReporter(); } exec(xatt._onIllegalAttr, (byte) 'A'); } } copyTemporaryReports(); _xdata = null; _parseResult = null; _xPos = xPos; return false; } copyTemporaryReports(); } _parseResult = null; if (xatt != null) { String xname = xatt.getName(); //let's register that we processed this attribute if (_attNames.contains(xatt.getName()) && !"$attr".equals(xname) && xatt._acceptQualifiedAttr == 'T') { //Both, the qualified and unqualified attributes are not allowed //with the option acceptQualifiedAttr: &{0} error(XDEF.XDEF559, qname); } if ("$attr".equals(xname)) { if (!_attNames.contains("$attr")) { _attNames.add(xname); } xname += qname; } boolean result = true; switch (xatt.minOccurs()) { case XOccurrence.ILLEGAL: // illegal break; // report as it is undefined case XOccurrence.IGNORE: // ignore _attName = null; _attURI = null; removeAttr(nsURI, qname); _attNames.add(xname); _xdata = null; _xPos = xPos; return true; default : {// required(1) or optional(0) _data = adata; debugXPos(XDDebug.INIT); if (xatt._init >= 0) {// execute "onInit" action _elemValue = _element; exec(xatt._init, (byte) 'A'); copyTemporaryReports(); } if (_data == null) { // value not exist if (xatt._onFalse >= 0) { String x = _data; _elemValue = _element; if (_clearReports) { clearTemporaryReporter(); } exec(xatt._onFalse, (byte) 'A'); updateAttrValue(xatt, x, nsURI, qname); } _attNames.add(xname); _parseResult = new DefParseResult(_data); } else { _elemValue = _element; //if the value is an ampty string and the option is //set to "acceptEmptyAttributes" at any level then //set the result of the check method to "true" (do NOT //report and/or process an error)! if (_data.isEmpty() && ((xatt._ignoreEmptyAttributes == 'A' || xatt._ignoreEmptyAttributes == 'P' && xatt.isOptional()) || xatt._ignoreEmptyAttributes == 0 && (_xElement._ignoreEmptyAttributes == 'A' ||_xElement._ignoreEmptyAttributes == 'P' && xatt.isOptional()) || _xElement._ignoreEmptyAttributes == 0 && (_rootChkDocument._ignoreEmptyAttributes=='A' || _rootChkDocument._ignoreEmptyAttributes=='P' && xatt.isOptional()))) { //accept empty attributes _attNames.add(xname); _parseResult = new DefParseResult(""); // empty attr } else { debugXPos(XDDebug.PARSE); //we are now sure the length is > 0 because if //the option was not set to "acceptEmptyAttributes" //then an empty attribute value was set to null and //the attribute had been ignored in attrWhitespaces! _attNames.add(xname); checkDatatype(xatt, false); } if (_parseResult.matches()) { // true clearTemporaryReporter(); if (_data != null) { if (!_data.equals(adata)) { if (nsURI != null) { _element.setAttributeNS(nsURI, qname, _data); } else { _element.setAttribute(qname, _data); } } } else { removeAttr(nsURI, qname); } debugXPos(XDDebug.ONTRUE); if (xatt._onTrue >= 0) { String x = _data; exec(xatt._onTrue, (byte) 'A'); updateAttrValue(xatt, x, nsURI, qname); } } else { // _parseResult.matches() == false // put error reports to chkElement debugXPos(XDDebug.ONFALSE); if (xatt._onFalse >= 0) { String x = _data; if (_clearReports) { clearTemporaryReporter(); } exec(xatt._onFalse, (byte) 'A'); updateAttrValue(xatt, x, nsURI, qname); } else { result = false; // an error found //copy reports from parsed result to //the temporary reporter. _scp.getTemporaryReporter().addReports( _parseResult.getReporter()); if (!chkTemporaryErrors()) { putTemporaryReport( //Incorrect value&{0} Report.error(XDEF.XDEF515)); } } copyTemporaryReports(); } } if (_data != null && !_data.equals(adata)) { if ((adata = attrWhitespaces(xatt, adata)) == null) { removeAttr(nsURI, qname); _attName = null; _attURI = null; _data = null; _parseResult = null; _attNames.add(xname); _xdata = null; _xPos = xPos; return result; // ignore empty attributes } } else { adata = _data; } if (_data != null && !_data.equals(adata)) { adata = attrWhitespaces(xatt, _data); } _attName = null; _attURI = null; _data = null; if (adata == null) { removeAttr(nsURI, qname); _xdata = null; _parseResult = null; _xPos = xPos; return !xatt.isRequired(); } if (nsURI != null) { _element.setAttributeNS(nsURI, qname, adata); } else { _element.setAttribute(qname, adata); } _attNames.add(xname); _xdata = null; _xPos = xPos; return result; } } } if ((_xElement._moreAttributes=='T' || _xElement._moreAttributes=='I') && (xatt == null || !xatt.isIllegal())) { //more attributes allowed, add attribute as it is //no X-definition for this attribute _parseResult = new DefParseResult(data); if (nsURI != null) { if (_xElement._moreAttributes=='I') { _element.removeAttributeNS(nsURI, qname); } else { _element.setAttributeNS(nsURI, qname, adata); if (_xComponent != null && getXMNode() != null && getXMNode().getXDPosition() != null) { _xComponent.xSetAttr(this, _parseResult); } } } else { if (_xElement._moreAttributes=='I') { _element.removeAttribute(qname); } else { _element.setAttribute(qname, adata); if (_xComponent != null && getXMNode() != null && getXMNode().getXDPosition() != null) { _xComponent.xSetAttr(this, _parseResult); } } } _attName = null; _attURI = null; _xdata = null; _xPos = xPos; return true; } debugXPos(XDDebug.ONILLEGALATTR); _data = adata = null; putTemporaryReport(Report.error( //Attribute not allowed XDEF.XDEF525, qname, getPosMod(getXDPosition(), _xPos))); if (xatt != null && xatt._onIllegalAttr >= 0) { if (_clearReports) { clearTemporaryReporter(); } exec(xatt._onIllegalAttr, (byte) 'T'); _parseResult = null; } else if (_xElement._onIllegalAttr >= 0) { if (_clearReports) { clearTemporaryReporter(); } _elemValue = _element; _data = adata; exec(_xElement._onIllegalAttr, (byte) 'E'); } copyTemporaryReports(); if (_data != null) { if (nsURI != null) { _element.setAttributeNS(nsURI, qname, adata); if (_xComponent != null && getXMNode() != null && getXMNode().getXDPosition() != null) { _xComponent.xSetAttr(this, _parseResult); } } else { _element.setAttribute(qname, adata); if (_xComponent != null && getXMNode() != null && getXMNode().getXDPosition() != null) { _xComponent.xSetAttr(this, _parseResult); } } } else { if (nsURI != null) { _element.removeAttributeNS(nsURI, qname); } else { _element.removeAttribute(qname); } } _attName = null; _attURI = null; _data = null; _parseResult = null; _xdata = null; _xPos = xPos; return false; } @Override /** Get name of actual node. * @return The name of node. */ public final String getNodeName() { if (getItemId() != XX_ELEMENT) { return _attName; } else { if (_element != null) { String s = _element.getLocalName(); return s == null ? _element.getNodeName() : s; } return null; } } @Override /** Get namespace URI of actual node. * @return namespace URI or null. */ public final String getNodeURI() { return (getItemId() != XX_ELEMENT) ? _attURI : _element != null ? _element.getNamespaceURI() : null; } @Override /** Store model variable. * @param name name of variable. * @param val value to be stored. */ final void storeModelVariable(final String name, final XDValue val) { int addr = findModelVariableOffset(name); if (addr < 0) { _parent.storeModelVariable(name, val); } else { _variables[addr] = val; } } @Override /** Load model variable. * @param name name of variable. * @return loaded value. */ public final XDValue loadModelVariable(final String name) { int addr = findModelVariableOffset(name); return addr < 0 ? _parent.loadModelVariable(name) : _variables[addr]; } @Override /** Set this element will be forgotten after being processed.*/ public final void forgetElement() {_forget = true;} @Override /** Increase reference counter by one. * @return The increased reference number. */ final int incRefNum() {return _actDefIndex < 0?0:++_counters[_actDefIndex];} @Override /** Decrease reference counter by one. * @return The increased reference number. */ final int decRefNum() {return _actDefIndex < 0?0:--_counters[_actDefIndex];} @Override /** Get reference counter of actual node * @return The reference number. */ public final int getRefNum() { return _actDefIndex < 0 ? 0 : _counters[_actDefIndex]; } @Override /** Get occurrence of actual element * @return The reference number. */ final int getOccurrence() {return _parent.getRefNum();} @Override /** Get ChkElement assigned to this node. * @return ChkElement assigned to this node. */ final ChkElement getChkElement() {return this;} @Override /** Prepare construction of the new element according to X-definition. * @param qname qualified name of the element (prefixed). * @param ns NameSpace URI of the element. * @return created check element object. */ public final XXElement prepareXXElementNS(final String ns, final String qname) { return createChkElement(ns == null ? _rootChkDocument._doc.createElement(qname) : _rootChkDocument._doc.createElementNS(ns,qname)); } @Override /** Prepare construction of the new element according to X-definition. * @param name Tag name of the element. * @return created check element object. */ public final XXElement prepareXXElement(final String name) { return prepareXXElementNS(null, name); } @Override /** Prepare construction of the new child according to X-definition. * @param model child model. * @return created XXElemnt element object. */ public final XXElement createChildXXElement(final XMElement model) { String ns = model.getNSUri(); String qname = model.getName(); Element el = ns == null ? _rootChkDocument._doc.createElement(qname) : _rootChkDocument._doc.createElementNS(ns, qname); return chkElem((XElement) model, el); } @Override /** Add the new attribute to the current XXElement. * @param name name of attribute. * @param data value of attribute. * @return true if attribute was created according to X-definition. */ public final boolean addAttribute(final String name, final String data) { return addAttributeNS(null, name, data); } @Override /** This method is called when the end of the current element attribute list * was parsed. The implementation may check the list of attributes and * may invoke appropriate actions. * @return true if element is compliant with X-definition. */ public final boolean checkElement() { _parseResult = null; if (_attsChecked) { return true; } _attsChecked = true; if (_ignoreAll) { // all checks are ignored (undef element) return true; } boolean result = true; //check if there are missing required attributes XData[] xattrs = (XData[]) _xElement.getAttrs(); String xPos = _xPos; for (int i = 0; i < xattrs.length && ! _nil; i++) { XData xatt = xattrs[i]; _xdata = xatt; String xname = xatt.getName(); if (xname.charAt(0) == '$') {//service attrs text, attr, textcontent continue; // TODO } boolean processed = false; // true if attribute was processed if (xatt.getNSUri() != null) { if (_element.hasAttributeNS(xatt.getNSUri(), xname.substring(xname.indexOf(':') + 1))) { processed = true; } } else if (_element.hasAttribute(xname)) { processed = true; } else if (_attNames.contains(xname)) { processed = true; } if (!processed) { _xPos = xPos + "/@" + xname; if (xatt._deflt >= 0) {// exec default method _data = null; _parseResult = null; _attName = xname; _elemValue = _element; XDValue value = exec(xatt._deflt, (byte) 'A'); if (value != null) { _data = value.toString(); checkDatatype(xatt, true); if (xatt.getNSUri() == null) { _element.setAttribute(xname, _data); } else { _element.setAttributeNS( xatt.getNSUri(), xname, _data); } } copyTemporaryReports(); if (xatt.getNSUri() == null) { if (_element.hasAttribute(xname)) { continue; } } else { if (_element.hasAttributeNS(xatt.getNSUri(), xname.substring(xname.indexOf(':') + 1))) { continue; } } } //missing attribute debugXPos(XDDebug.ONABSENCE); if (xatt._onAbsence >= 0) { // onAbsence method _data = null; _parseResult = null; _attName = xname; _elemValue = _element; Report rep = null; if (xatt.minOccurs() == XData.REQUIRED) { //Missing required attribute &{0} rep = Report.error(XDEF.XDEF526, xname); putTemporaryReport(rep); } if (!_attNames.contains(xatt.getName())) { String uri = xatt.getNSUri(); // was not processed _xPos = xPos +"/@" + xname; exec(xatt._onAbsence, (byte) 'A'); if (_data != null) { checkDatatype(xatt, true); if (uri == null) { _element.setAttribute(xname, _data); } else { _element.setAttributeNS(uri, xname, _data); } _attNames.add(xname); } if (uri == null && _element.hasAttribute(xname) || uri != null && _element.hasAttributeNS(uri, xname.substring(xname.indexOf(':') + 1))) { //remove the message "missing" removeTemporaryReport(rep); continue; // attribute exists, don't invoke default } } if (xatt._deflt < 0) { copyTemporaryReports(); continue; // skip default method } removeTemporaryReport(rep); // don't report "missing" twice copyTemporaryReports(); } if (xatt.minOccurs() == XData.REQUIRED) { //no method called; put error error(XDEF.XDEF526, xname);//Missing required attribute &{0} result = false; } } _parseResult = null; if (xatt._onStartElement >= 0) { // execute onStartElement action _data = null; _attName = xname; _elemValue = _element; exec(xatt._onStartElement, (byte) 'A'); } } _xPos = xPos; _parseResult = null; debugXPos(XDDebug.ONSTARTELEMENT); if (_xElement._onStartElement >= 0) {// exec on end of attr list _elemValue = _element; exec(_xElement._onStartElement, (byte) 'E'); copyTemporaryReports(); updateElement(_elemValue); if (_elemValue == null) { result = false; } } if (_scp.getXmlStreamWriter() != null) { try { _scp.getXmlStreamWriter().writeElementStart(_element); } catch (SRuntimeException ex) { putReport(ex.getReport()); } } _xdata = null; _parseResult = null; return result; } @Override /** Add new element as a child of the current element. * Checks all attributes and child elements for occurrence. * @return true if element was added and complies to X-definition. */ public final boolean addElement() { if (_nil) { debugXPos(XDDebug.FINALLY); if (_xElement._finaly >= 0) { _elemValue = _element; exec(_xElement._finaly, (byte) 'E'); copyTemporaryReports(); updateElement(_elemValue); } _parent.incRefNum(); closeChkElement(); return true; } if (_ignoreAll) { //all checks are ignored (undef element) if (_element!=null && (_elemValue = _parent.getElement())!=null) { _elemValue.removeChild(_element); } //let's garbage collector do the job _chkChildNodes = null; _attNames = null; _xElement = null; _element = null; _defList = new XNode[0]; _counters = new int[0]; _xPos = null; _elemValue = null; _sourceElem = null; _data = null; _parseResult = null; _userObject = null; return true; } finishModel(); boolean error = false; if (_element != null) { int n = _parent.getRefNum(); if (n >= _xElement.maxOccurs()) { debugXPos(XDDebug.ONEXCESS); if (_xElement._onExcess >= 0) { _elemValue = _element; exec(_xElement._onExcess, (byte) 'E'); copyTemporaryReports(); updateElement(_elemValue); } if (_element != null) { _parent.incRefNum(); //Maximum occurrence limit of &{0} exceeded error(XDEF.XDEF558, "element " + _element.getTagName()); error = true; } } else { _parent.incRefNum(); } } if (_parent._parent != null && _xElement._xon > 0) {//not root; gen XON if (!_forget && _xElement._forget != 'T' && _element != null) { ChkElement chkEl = (ChkElement) _parent; Object value = X_MAP.equals(_element.getLocalName()) ?_xonMap : X_ARRAY.equals(_element.getLocalName()) ?_xonArray : _xonValue; if (chkEl._xonMap != null) { chkEl._xonMap.put(_xonKey, value); } else if (chkEl._xonArray != null) { chkEl._xonArray.add(value); } else { chkEl._xonValue = value; } } } if (_element != null) { String name; String xPos = _xPos; XData[] xattrs = (XData[]) _xElement.getAttrs(); int anyAttrs = 0; for (XData xatt : xattrs) { _xdata = xatt; name = xatt.getName(); if ("$attr".equals(name)) {// any attribute _xPos = xPos; NamedNodeMap nm = _element.getAttributes(); for (int j = 0, k = nm.getLength(); j < k; j++) { Node item = nm.item(j); name = item.getNodeName(); int n = name.indexOf(':'); if (n >= 0 && item.getNamespaceURI() != null) { name = name.substring(n+1); } _attName = name; if (!_attNames.contains(name)) { debugXPos(XDDebug.FINALLY); if (xatt._finaly >= 0) { String orig = _data = nm.item(j).getNodeValue(); exec(xatt._finaly, (byte) 'A'); copyTemporaryReports(); anyAttrs++; if (_data == null) { _element.removeAttributeNode( (Attr) item); anyAttrs--; } else if (!_data.equals(orig)) { ((Attr) item).setValue(_data); _data = null; _parseResult = null; } } else { anyAttrs++; } } } if (anyAttrs < xatt.getOccurence().minOccurs()) { //Minimum number of attributes declared as "xd:attr" //was not reached putTemporaryReport(Report.error(XDEF.XDEF531)); } else if (anyAttrs > xatt.getOccurence().maxOccurs()) { //Maximum number of attributes declared as "xd:attr" //was exceeded putTemporaryReport(Report.error(XDEF.XDEF532)); } } else if (name.charAt(0) != '$') {// normal attribute _xPos = xPos + "/@" + name; debugXPos(XDDebug.FINALLY); if (xatt._finaly >= 0) { if (!_attNames.contains(name)) { //TODO } _attName = name; String orig; String uri; if ((uri = xatt.getNSUri()) == null) { _node = _element.getAttributeNode(name); orig = _data = _element.getAttribute(name); } else { _node = _element.getAttributeNodeNS(uri, name.substring(name.indexOf(':') + 1)); orig = _data = _element.getAttributeNS( uri, name.substring(name.indexOf(':') + 1)); } exec(xatt._finaly, (byte) 'A'); _node = null; copyTemporaryReports(); if (!_data.equals(orig)) { if (uri == null) { if (_data == null) { _element.removeAttribute(name); } else { _element.setAttribute(name, _data); } } else { if (_data == null) { _element.removeAttributeNS(uri, name.substring(name.indexOf(':') + 1)); } else { _element.setAttributeNS(uri, name, _data); } } } _data = null; _parseResult = null; } } } XData xtxt; if ((xtxt = _xElement.getDefAttr("$text", -1)) != null) { _xPos = xPos + "/text()"; if (_numText < xtxt.minOccurs()) { debugXPos(XDDebug.ONABSENCE); if (xtxt._onAbsence >= 0) { _elemValue = _element; _data = null; _parseResult = null; exec(xtxt._onAbsence, (byte) 'T'); copyTemporaryReports(); if (_data != null) { for (Node n=_element.getLastChild(); n != null;) { Node m = n.getPreviousSibling(); _element.removeChild(n); n = m; } appendTextNode(_data, xtxt); _numText++; if (xtxt._finaly >= 0) { exec(xtxt._finaly, (byte) 'T'); } } } if (_numText < xtxt.minOccurs()) { if (!_nil || _numText > 0) { error(XDEF.XDEF527, //Missing required text getPosMod(xtxt.getXDPosition(), null)); } } } if (_numText > xtxt.maxOccurs() && !xtxt.isIllegal()) { //Maximum number of text nodes declared as "xd:text" // was exceeded error(XDEF.XDEF533, getPosMod(xtxt.getXDPosition(), null)); } } if ((xtxt = _xElement.getDefAttr("$textcontent", -1)) != null) { _xPos = xPos + "/text()"; String orig = _data = KXmlUtils.getTextContent(_element); if (!orig.isEmpty()) { _numText = 1; debugXPos(XDDebug.PARSE); XDValue item = xtxt._check >= 0 ? exec(xtxt._check, (byte) 'T') : new DefBoolean(true); if (item != null && (item.getItemId() == XD_PARSERESULT ? ((XDParseResult) item).matches() : item.booleanValue())) { clearTemporaryReporter(); debugXPos(XDDebug.ONTRUE); if (xtxt._onTrue >= 0) { String x = _data; exec(xtxt._onTrue, (byte) 'T'); copyTemporaryReports(); if (x != _data) { // _data was changed, even equal exec(xtxt._check, (byte) 'T'); copyTemporaryReports(); } } } else { debugXPos(XDDebug.ONFALSE); if (xtxt._onFalse >= 0) { String x = _data; if (_clearReports) { clearTemporaryReporter(); } exec(xtxt._onFalse, (byte) 'T'); if (x != _data) { // _data was changed, even equal exec(xtxt._check, (byte) 'T'); } } else { if (!chkTemporaryErrors()) { //Value error putTemporaryReport(Report.error(XDEF.XDEF515)); } } copyTemporaryReports(); if (!orig.equals(_data)) { for (Node n=_element.getLastChild(); n != null;) { Node m = n = n.getPreviousSibling(); if (n.getNodeType() == Node.TEXT_NODE ||n.getNodeType()==Node.CDATA_SECTION_NODE){ _element.removeChild(n); } n = m; } appendTextNode(_data, xtxt); } } debugXPos(XDDebug.FINALLY); if (xtxt._finaly >= 0) { exec(xtxt._finaly, (byte) 'T'); copyTemporaryReports(); } } else if (_numText < xtxt.minOccurs()) { debugXPos(XDDebug.ONABSENCE); if (xtxt._onAbsence >= 0) { _elemValue = _element; _data = null; _parseResult = null; exec(xtxt._onAbsence, (byte) 'T'); copyTemporaryReports(); if (_data != null) { appendTextNode(_data, xtxt); _numText++; debugXPos(XDDebug.FINALLY); if (xtxt._finaly >= 0) { _data = KXmlUtils.getTextValue(_element); exec(xtxt._finaly, (byte) 'T'); copyTemporaryReports(); } } } else if (_numText < xtxt.minOccurs() && !_nil) { error(XDEF.XDEF527,//Missing required text getPosMod(xtxt.getXDPosition(), null)); } } } _xdata = null; _parseResult = null; _xPos = xPos; } debugXPos(XDDebug.FINALLY); if (_xElement._finaly >= 0) { _elemValue = _element; exec(_xElement._finaly, (byte) 'E'); copyTemporaryReports(); updateElement(_elemValue); } closeChkElement(); return !error; } @Override /** Add new Text node to current element. * @param data The value of text node. * @throws SRuntimeException if an error occurs. * @return true if text node is compliant with X-definition. */ public final boolean addText(final String data) { if (_ignoreAll || _element == null) { return true; //all checks are ignored (undef element) } if (_nil) { if (data.trim().isEmpty()) { return true; } else { //Text value not declared error(XDEF.XDEF534); return false; } } if ((_data = textWhitespaces(null, data)).isEmpty()) { _data = null; _parseResult = null; return true; } int nextDefIndex = _nextDefIndex, actDefIndex = _actDefIndex; _data = data; XData xtxt = (XData) findXNode(null); if (xtxt != null) { _data = textWhitespaces(xtxt, data); } XData xtxt1 = xtxt; _xdata = xtxt; String value = _data; String xPos = _xPos; String txtname = getTextPathIndex(actDefIndex); _xPos += "/text()" + txtname; if (xtxt1 != null) {// found txtname = "$text" + txtname; _attNames.add(txtname); if (value.isEmpty()) { debugXPos(XDDebug.ONABSENCE); if (xtxt1._onAbsence >= 0) { _elemValue = _element; exec(xtxt1._onAbsence, (byte) 'T'); } debugXPos(XDDebug.FINALLY); if (_data != null) { if (xtxt1._finaly >= 0) { _elemValue = _element; exec(xtxt1._finaly, (byte) 'T'); } } value = _data == null ? "" : _data; if (value.isEmpty()) { _data = null; _parseResult = null; _nextDefIndex = nextDefIndex; _actDefIndex = actDefIndex; _xPos = xPos; _xdata = null; return true; } } else { if (value.isEmpty()) { _data = null; _parseResult = null; _nextDefIndex = nextDefIndex; _actDefIndex = actDefIndex; _xPos = xPos; _xdata = null; return true; } } _numText++; } else {// not found if (value.trim().isEmpty()) { _data = null; _parseResult = null; // we ignore empty text _nextDefIndex = nextDefIndex; _actDefIndex = actDefIndex; _xPos = xPos; _xdata = null; return true; } _numText++; if (_xElement.hasDefAttr("$text")) { _nextDefIndex = nextDefIndex; _actDefIndex = actDefIndex; xtxt1 = _xElement.getDefAttr("$text", -1); } else if (_xElement._moreElements != 'T' && _xElement._moreElements != 'I' && !_xElement.hasDefAttr("$textcontent") && _xElement._moreText != 'T' && _xElement._moreText != 'I') { debugXPos(XDDebug.ONILLEGALTEXT); if (_xElement._onIllegalText >= 0) { _elemValue = _element; //Text value not declared putTemporaryReport(Report.error(XDEF.XDEF534)); exec(_xElement._onIllegalText, (byte) 'E'); copyTemporaryReports(); } else { if (_xElement._moreText == 'I') { _data = null; } else { error(XDEF.XDEF534); //Text value not declared } } _xdata = null; _parseResult = null; _xPos = xPos; return false; } else {// moreElements, textcontent if (_xElement._moreText == 'I') { _data = null; _xdata = null; _parseResult = null; _xPos = xPos; return true; } xtxt = xtxt1 = new XData(// dummy text "$text", null, _xElement.getXDPool(), XMTEXT); if (_xElement.hasDefAttr("$textcontent")) { //copy option cdata! xtxt1._cdata = _xElement.getDefAttr("$textcontent", -1)._cdata; } } } _data = value; debugXPos(XDDebug.INIT); if (xtxt1._init >= 0) { // execute "onInit" action _elemValue = _element; exec(xtxt1._init, (byte) 'T'); copyTemporaryReports(); } int obligation; switch (obligation = xtxt1.minOccurs()) { case XOccurrence.IGNORE: // ignore _data = null; _parseResult = null; _xdata = null; _xPos = xPos; return true; case XOccurrence.ILLEGAL: // illegal if (value.isEmpty()) { _xdata = null; _xPos = xPos; return true; } debugXPos(XDDebug.ONILLEGALTEXT); if (xtxt1._onIllegalText >= 0) { if (!_clearReports) { //Illegal text putTemporaryReport(Report.error(XDEF.XDEF528)); } _elemValue = _element; _data = null; exec(xtxt1._onIllegalText, (byte) 'T'); _parseResult = null; copyTemporaryReports(); _xdata = null; _xPos = xPos; return true; } error(XDEF.XDEF528); //Illegal text _data = null; _parseResult = null; _xdata = null; _xPos = xPos; return false; default : // required(1) or optional(0) if (value.isEmpty()) {// the text node without text ??? _data = null; _parseResult = null; debugXPos(XDDebug.ONFALSE); if (xtxt1._onFalse >= 0) {// value not exist _elemValue = _element; if (_clearReports) { clearTemporaryReporter(); } exec(xtxt1._onFalse, (byte) 'T'); if (_data!=null) { exec(xtxt1._check, (byte) 'T'); } copyTemporaryReports(); } _parseResult = new DefParseResult(_data); } else { debugXPos(XDDebug.PARSE); if (xtxt1._check >= 0) { XDValue item; _elemValue = _element; item = exec(xtxt1._check, (byte) 'T'); if (item.getItemId() == XD_PARSERESULT) { _parseResult = (XDParseResult) item; } else { _parseResult = new DefParseResult(_data); if (!item.booleanValue()) { //XDEF515=Value error&{0}{ :} _parseResult.putDefaultParseError(); } } if (_parseResult.matches()) { clearTemporaryReporter(); // clear all error reports debugXPos(XDDebug.ONTRUE); if (xtxt1._onTrue >= 0) { String s = _data; exec(xtxt1._onTrue, (byte) 'T'); if (s != _data) {//_data was changed, even equal item = exec(xtxt1._check, (byte) 'T'); if (item.getItemId() == XD_PARSERESULT) { _parseResult = (XDParseResult) item; } else { _parseResult = new DefParseResult(_data); if (!item.booleanValue()) { //XDEF515=Value error&{0}{ :} _parseResult.putDefaultParseError(); } } } copyTemporaryReports(); } } else { //call put error reports to chkElement if (!chkTemporaryErrors()) { for (Report rep: _parseResult.getReporter()) { putTemporaryReport(rep); } } debugXPos(XDDebug.ONFALSE); if (xtxt1._onFalse >= 0) { if (_clearReports) { clearTemporaryReporter(); } String x = _data; exec(xtxt1._onFalse, (byte) 'T'); if (x != _data) {//_data was changed, even equal exec(xtxt1._check, (byte) 'T'); } } copyTemporaryReports(); } } else { _parseResult = new DefParseResult(); debugXPos(XDDebug.ONTRUE); if (xtxt1._onTrue >= 0) { // if check exception not defined we call onTrue // action for value which is not null _elemValue = _element; String x = _data; exec(xtxt1._onTrue, (byte) 'T'); copyTemporaryReports(); if (x != _data) {//_data was changed, even equal exec(xtxt1._check, (byte) 'T'); } } } } value = _data != null && !_data.equals(value) ? textWhitespaces(xtxt1, _data) : _data; if (value != null) { debugXPos(XDDebug.FINALLY); if (xtxt1._finaly >= 0) { _elemValue = _element; Node txt = appendTextNode(_data = value, xtxt1); exec(xtxt1._finaly, (byte) 'T'); if ((value = _data) == null || value.isEmpty()) { _element.removeChild(txt); } else { txt.setNodeValue(value); } } else if (!value.isEmpty()) { appendTextNode(value, xtxt1); if (_xComponent != null && _parseResult.getParsedValue() != null) { _xComponent.xSetText(this, _parseResult); } } if (value != null && !value.isEmpty()) { if (_actDefIndex >= 0 && _defList[_actDefIndex].getKind()==XMTEXT) { int n = xtxt == xtxt1 ? incRefNum() : getRefNum(); if (_actDefIndex > 0 && n > xtxt1.maxOccurs()) { //Maximum occurrence limit of &{0} exceeded error(XDEF.XDEF558, "text"); } } } _data = null; _xdata = null; _xPos = xPos; return true; } else { _data = null; _parseResult = null; debugXPos(XDDebug.FINALLY); if (xtxt1._finaly >= 0) { _elemValue = _element; exec(xtxt1._finaly, (byte) 'T'); } _xdata = null; _xPos = xPos; return obligation != XData.REQUIRED; } } } @Override /** Add new Comment node to current element. * @param data The value of Comment node. * @return true if Comment node is compliant with X-definition. */ //TODO public final boolean addComment(final String data) {return true;} @Override /** Add new Processing instruction node to current element. * @param name The name of the PI node. * @param X The value of instruction part of the PI node. * @throws SRuntimeException if an error occurs. * @return true if PI node is compliant with X-definition. */ //TODO public final boolean addPI(final String name, final String x) {return true;} @Override /** Get text value of this node. * @return The string with value of node. */ public final String getTextValue() { return (getItemId() != XX_ELEMENT) ? _data : null; } @Override /** Set text value to this node. * @param data the text value to be set. */ public final void setTextValue(final String data) { if (getItemId() != XX_ELEMENT) { _data = data; } else { //Illegal use of method: &{0} throw new SRuntimeException(SYS.SYS083, "setText"); } } @Override public final short getItemId() { return _mode == 'T' ? XX_TEXT : _mode == 'A'? XX_ATTR : XX_ELEMENT; } @Override /** Get ID of the type of value * @return enumeration item of this type. */ public final XDValueType getItemType() { return _mode == 'T' ? XXTEXT : _mode == 'A'? XXATTR : XXELEMENT; } @Override /** Get attribute with namespace from XXElement. * @param uri The namespace of attribute. * @param name The local name of attribute. * @return value of attribute or the empty string if the attribute is legal * otherwise throws the SRuntimeException. * @throws SRuntimeException if the attribute is not legal in actual model. */ public final String getAttributeNS(final String uri, final String name) { Attr att = (uri == null) ? _element.getAttributeNode(name) : _element.getAttributeNodeNS(uri,name); if (att != null) { return att.getValue(); } //attribute not exist in element. XMElement xel = getXMElement(); XMData xatt = (uri == null) ? xel.getAttr(name) : xel.getAttrNS(uri, name); //prepare path for error message if (xatt != null) { if (xatt.isIllegal()) { //Attempt to get illegal item throw new SRuntimeException(XDEF.XDEF582, getXPos() + "/@" + name); } return null; //attribute is defined but not exists } else if (xel.hasOtherAttrs()) { //If X-definition has a VARIABLE_PART it makes no sense //to check it more. return null; } //Attempt to get undeclared item throw new SRuntimeException(XDEF.XDEF581, getXPos() + "/@" + name); } @Override /** Get attribute from the XXElement object. * @param name The name of attribute. * @return The value of attribute or the empty string if the value * doesn't exist or return null if required attribute is defined in the * XXElement, however it does not exist in the actual element. * @throws SRuntimeException if required attribute is not defined * in the X-definition. */ public final String getAttribute(final String name) { return getAttributeNS(null, name); } @Override /** Get work element value. * @return work element value. */ public final Element getElemValue() {return _elemValue;} @Override /** Set work element value. * @param e The element. */ final void setElemValue(final Element e) {_elemValue = e;} @Override /** Get root XXElement. * @return root XXElement node. */ public final XXElement getRootXXElement(){return _rootChkDocument._chkRoot;} @Override /** Get actual associated XXElement. * @return root XXElement node. */ public final XXElement getXXElement() {return this;} @Override /** Get associated XML node. * @return the associated XML node. */ public final Node getXMLNode() {return _node;} @Override /** Get namespace context of corresponding XElement. * @return namespace context of the parent element. */ public final KNamespace getXXNamespaceContext() { return _xElement.getXDNamespaceContext(); } @Override /** Check if attribute is legal in the XXElement. * @param name The name of attribute. * @return true if and only if the attribute is legal in the * XXElement, otherwise return false. */ public final boolean checkAttributeLegal(final String name) { XData xatt = getXAttr(name); return xatt != null && !xatt.isIllegal(); } @Override /** Check if attribute with given namespace is legal in the XXElement. * @param uri namespace URI. * @param name name of attribute (optionally with prefix). * @return true if and only if the attribute is legal in the * XXElement, otherwise return false. */ public final boolean checkAttributeNSLegal(final String uri, final String name) { XData xatt = uri == null || uri.isEmpty() ? getXAttr(name) : getXAttr(uri, name); return xatt != null && !xatt.isIllegal(); } @Override /** Get array of XXNodes or null. * @return array of XXNodes or null. */ public final XXNode[] getChildXXNodes() { XXNode[] result = new XXNode[_chkChildNodes.size()]; _chkChildNodes.toArray(result); return result; } @Override final List getChkChildNodes() {return _chkChildNodes;} @Override /** Get model of the processed data object. * @return model of the processed data object. */ public final XMData getXMData() {return _xdata;} @Override /** Get actual model. * @return actual model. */ public final XMNode getXMNode() { /** mode: 'C' - comment, 'E' - element, 'A' - attribute, 'T' - text, * 'D' - document, 'P' - processing instruction,'U' undefined. */ return (_mode == (byte) 'A' || _mode == (byte) 'T') ? (XMNode) _xdata : (XMNode) _xElement; } @Override /** Get XComponent. * @return The XComponent object (may be null). */ public final XComponent getXComponent() {return _xComponent;} @Override /** Set XComponent. * @param x XComponent object. */ public final void setXComponent(final XComponent x) {_xComponent = x;} @Override /** Get XON result of processed Element model. * @return result of XON parsing. */ public Object getXon() { return _xonArray!=null ? _xonArray : _xonMap!=null ?_xonMap : _xonValue; } //////////////////////////////////////////////////////////////////////////////// /** This is object containing actual selector state. */ final class SelectorState extends XOccurrence { /** Previous selector */ SelectorState _prev; /** Saved counters */ int[] _savedCounters; /** Where selector begins */ int _begIndex; /** Where selector ends */ int _endIndex; /** Index of first child in selector list */ int _firstChild; /** Number of selector repetitions */ int _count; /** Address of "finally" method or -1 */ int _finallyCode; /** Kind of selector */ short _kind; /** True if first item of sequence is selective */ boolean _selective; /** True if selector is ignorable */ boolean _ignorable; /** True if an error was reported */ boolean _error; /** True if an item was recognized */ boolean _occur; /** Create new SelectorState. * @param prev previous SelectorState in chain or null. * @param xs Selector from which SelectorState will be created. */ SelectorState(final SelectorState prev, final XSelector xs) { super(xs.minOccurs(), xs.maxOccurs()); _prev = prev; _kind = xs.getKind(); _begIndex = xs._begIndex; _endIndex = xs._endIndex; _ignorable = xs._ignorable; _selective = xs._selective; _count = 0; _finallyCode = xs._finaly; _firstChild = _element.getChildNodes().getLength(); } /** Save actual counters and clear them. */ final boolean saveAndClearCounters() { boolean result = false; int len = _endIndex - (_begIndex + 1); if (_savedCounters == null) { _savedCounters = new int[len]; System.arraycopy(_counters, _begIndex + 1, _savedCounters, 0, len); for (int i = 0; i < len; i++) { if (_savedCounters[i] > 0) { result = true; break; } } } else { for (int i = 0, j = _begIndex + 1; i < len; i++,j++) { int z; if ((z = _counters[j]) > 0) { _savedCounters[i] += z; result = true; } } } Arrays.fill(_counters, _begIndex + 1, _endIndex, 0); _occur = false; return result; } /** Update counters form saved counters. */ final boolean updateCounters() { if (_savedCounters == null) { return false; } boolean result = false; int len = _endIndex - (_begIndex + 1); for (int x = _begIndex + 1, y = 0; y < len; x++, y++) { int z; if ((z = _savedCounters[y]) > 0) { _counters[x] += z; result = true; } } _savedCounters = null; return result; } } }