# XNodeRefImpl.java

    /*
    ** Module   : XNodeRefImpl
    ** Abstract : DOM XML Node reference resource type implementation.
    **
    ** Copyright (c) 2013-2025, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
    ** 001 EVL 20130204 Created initial version. Major rework to use w3c backend.
    ** 002 CA  20130221 Added LOCAL-NAME support.
    ** 003 EVL 20130222 Adding NODE-VALUE attribute and SET-ATTRIBUTE-NODE method.
    ** 004 EVK 20130531 Adding NODE-VALUE attribute and GET-PARENT, GET-ATTRIBUTE-NODE, NORMALIZE
    **                  LONGCHAR-TO-NODE-VALUE, NODE-VALUE-TO-LONGCHAR, CLONE-NODE methods.
    ** 005 CA  20130930 Changes related to determining resource type from LegacyResource annotation.
    ** 006 HC  20131215 Implemented UNIQUE-ID attribute.
    ** 007 CA  20140104 Changed getOwnerDocument to allow restricted access (required by SOAP Fault
    **                  Detail resource).
    ** 008 CA  20160428 Fixed getOwnerDocument - must return a XDocumentImpl handle.
    ** 009 OM  20201120 Renamed NAMESPACE-PREFIX setter to fit buffer attributes naming convention.
    ** 010 CA  20220605 Fixed 'getLocalName' to return the node name if it is not namespace-aware.
    ** 011 CA  20230720 NAMESPACE-PREFIX setter with an unknown value must raise error 4083.
    **                  LOCAL-NAME must return empty space if is null.
    ** 012 ICP 20250129 Used logical and character constants to leverage cached instances.
    */

    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program is distributed in the hope that it will be useful,
    ** but WITHOUT ANY WARRANTY; without even the implied warranty of
    ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ** GNU Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.xml;

    import org.w3c.dom.*;
    import com.goldencode.p2j.util.*;
    import com.goldencode.p2j.util.UniqueIdGenerator.IdKind;

    /**
     * Implementation of the X-Node-Reference specific features of the Progress
     * DOM XML support objects.
     */
    public class XNodeRefImpl
    extends XEntityImpl
    implements XNodeRef
    {
       /** The key for a restricted document mapping in the {@link Document}'s user-data. */
       public static final String RESTRICTED_DOCUMENT = "RESTRICTED_DOCUMENT";

       /**
        * Default constructor.
        */
       public XNodeRefImpl()
       {
          super(IdKind.X_NODEREF);
       }

       /**
        * Special constructor accepting the namespace URI specification to make
        * another node reference case.
        *
        * @param    node
        *           The node associated with this node.
        */
       public XNodeRefImpl(Node node)
       {
          super(node, IdKind.X_NODEREF);
       }

       // Attributes
       /**
        * Getting the attribute names available for current node reference.
        *
        * @return   The comma separated character string of all available
        *           attributed.
        */
       @Override
       public character getAttributeNames()
       {
          if (!isInitialized())
          {
             invalidNodeError("ATTRIBUTE-NAMES");
             return new character();
          }

          StringBuilder sb = new StringBuilder();
          NamedNodeMap nnm = getNode().getAttributes();
          if (nnm == null)
          {
             return new character("");
          }
          int iSize = nnm.getLength();
          for (int i = 0; i < iSize; i++)
          {
             sb.append(((Attr)nnm.item(i)).getName());
             if (i < iSize - 1)
             {
                sb.append(',');
             }
          }

          return new character(sb.toString());
       }

       /**
        * Getting the value of the XML node value attribute.
        *
        * @return   The character value of the node reference.
        */
       @Override
       public character getNodeValue()
       {
          if (!isInitialized())
          {
             invalidNodeError("NODE-VALUE");
             return new character();
          }
          try
          {
             String nodeValue = getNode().getNodeValue();
             return new character(nodeValue == null ? "" : nodeValue);
          }
          catch (DOMException e)
          {
             if (e.code == DOMException.DOMSTRING_SIZE_ERR)
             {
                commonDomError("NODE-VALUE", "Too long node-value");
             }
             return new character();
          }
       }

       /**
        * Setting the value of the XML node value attribute.
        *
        * @param    value
        *           The new value of the node value attribute.
        */
       @Override
       public void setNodeValue(character value)
       {
          if (value.isUnknown())
          {
             invalidArgumentAssignError("NODE-VALUE");
             return;
          }
          if (!isInitialized())
          {
             String[] errMsg =
                {
                   "X-NODEREF must be associated with a valid X-DOCUMENT in order to use it in " +
                      "method NODE-VALUE",
                   "Unable to set attribute NODE-VALUE in widget  of type X-NODEREF"
                };
             int[] errorCodes = { 9102, 3131 };
             ErrorManager.recordOrShowError(errorCodes, errMsg, false, false, true);
             return;
          }

          Node node = getNode();
          int nodeType = node.getNodeType();
          if (nodeType == Node.ELEMENT_NODE || nodeType == Node.ENTITY_REFERENCE_NODE)
          {
             String[] errMsg =
                {
                   "X-NODEREF or X-DOCUMENT NODE-VALUE got an error: An " +
                      "attempt was made to modify an object where modifications are not allowed",
                   "Unable to set attribute NODE-VALUE in widget  of type X-NODEREF"
                };
             int[] errorCodes = { 9082, 3131 };
             ErrorManager.recordOrShowError(errorCodes, errMsg, false, false, true);
             return;
          }

          try
          {
             node.setNodeValue(value.toStringMessage());
          }
          catch (DOMException e)
          {
             if (e.code == DOMException.NO_MODIFICATION_ALLOWED_ERR)
             {
                String[] errMsg =
                   {
                      "X-NODEREF or X-DOCUMENT NODE-VALUE got an error: An " +
                         "attempt was made to modify an object where modifications are not allowed",
                      "Unable to set attribute NODE-VALUE in widget  of type X-NODEREF"
                   };
                int[] errorCodes = { 9082, 3131 };
                ErrorManager.recordOrShowError(errorCodes, errMsg, false, false, true);
                return;
             }
          }
       }

       /**
        * Setting the value of the XML node value attribute.
        *
        * @param    value
        *           The new value of the node value attribute.
        */
       @Override
       public void setNodeValue(String value)
       {
          setNodeValue(new character(value));
       }

       /**
        * Creating the new node within the current Document object.
        *
        * @param xNodeRef The valid handle class instance to use for the cloned XML node.
        * @param deep     A logical that if <code>true</code> specifies that the whole sub-trees is to be
        *                 copied. The default value is {@code false}.
        *
        * @return The <code>true</code> in case of success <code>false</code> otherwise.
        */
       @Override
       public logical cloneNode(handle xNodeRef, logical deep)
       {
          if (!isInitialized())
          {
             invalidNodeError("CLONE-NODE");
             return new logical(false);
          }

          if (!isXNodeHandle(xNodeRef))
          {
             invalidArgumentError("CLONE-NODE");
             return new logical(false);
          }

          Node node = getNode();
          Node clonedNode = node.cloneNode(deep.booleanValue());
          setNodeToHandle(xNodeRef, clonedNode);

          return xNodeRef.isValid();
       }

       /**
        * Creating the new node within the current Document object.
        *
        * @param xNodeRef The valid handle class instance to use for the cloned XML node.
        * @param deep     A logical that if <code>true</code> specifies that the whole sub-trees is to
        *                 be copied. The default value is {@code false}.
        *
        * @return The <code>true</code> in case of success <code>false</code> otherwise.
        */
       @Override
       public logical cloneNode(handle xNodeRef, boolean deep)
       {
          return cloneNode(xNodeRef, new logical(deep));
       }

       /**
        * Getting the Parent of current node.
        *
        * @param    hNoderefParent
        *           A valid handle to assign parent XML node.
        *
        * @return   The <code>true</code> in case of success
        *           <code>false</code> otherwise.
        */
       @Override
       public logical getParent(handle hNoderefParent)
       {
          if (!isInitialized())
          {
             invalidNodeError("GET-PARENT");
             return new logical(false);
          }
          if (!isXNodeHandle(hNoderefParent))
          {
             invalidArgumentError("GET-PARENT");
             return new logical(false);
          }

          Node node = getNode().getParentNode();
          setNodeToHandle(hNoderefParent, node);

          return hNoderefParent.isValid();
       }

       // Methods
       /**
        * Deleting the internals of the current X-Node-Reference.
        *
        * @return   The <code>true</code> in case of success
        *           <code>false</code> otherwise.
        */
       @Override
       public logical deleteNode()
       {
          if (!isInitialized())
          {
             invalidNodeError("DELETE-NODE");
             return new logical(false);
          }

          Node node = getNode();
          Node parentNode = node.getParentNode();
          if (parentNode == null)
          {
             invalidNodeError("DELETE-NODE");
             return new logical(false);
          }
          try
          {
             parentNode.removeChild(node);
          }
          catch (DOMException e)
          {
             if (e.code == DOMException.NOT_FOUND_ERR)
             {
                invalidNodeError("DELETE-NODE");
             }
             else if (e.code == DOMException.NO_MODIFICATION_ALLOWED_ERR)
             {
                invalidNodeError("DELETE-NODE");
             }
             return new logical(false);
          }

          return new logical(true);
       }

       /**
        * Getting the attribute value for a given attribute name.
        *
        * @param    name
        *           The name of the attribute to return.
        *
        * @return   The comma separated character string of all available
        *           attributed.
        */
       @Override
       public character getAttribute(String name)
       {
          return getAttribute(new character(name));
       }

       /**
        * Getting the attribute value for a given attribute name.
        *
        * @param    name
        *           The name of the attribute to return.
        *
        * @return   Value as a character string, or the empty string
        *           if that attribute does not have a specified or default value.
        */
       @Override
       public character getAttribute(character name)
       {
          if (!isInitialized())
          {
             invalidNodeError("GET-ATTRIBUTE");
             return new character();
          }
          if (name.isUnknown())
          {
             invalidArgumentError("GET-ATTRIBUTE");
             return new character();
          }

          Node node = getNode();
          String result = null;
          if (node.getNodeType() == Node.ELEMENT_NODE)
          {
             Element elementNode = (Element) node;
             result = elementNode.getAttribute(name.toStringMessage());
          }

          return new character((result == null) ? "" : result);
       }

       /**
        * Setting the attribute value for a given attribute name.
        *
        * @param    name
        *           The name of the attribute to change.
        * @param    value
        *           The new value for the given attribute name.
        *
        * @return   The <code>true</code> in case of success
        *           <code>false</code> otherwise.
        */
       @Override
       public logical setAttribute(String name, String value)
       {
          return setAttribute(new character(name), new character(value));
       }

       /**
        * Setting the attribute value for a given attribute name.
        *
        * @param    name
        *           The name of the attribute to change.
        * @param    value
        *           The new value for the given attribute name.
        *
        * @return   The <code>true</code> in case of success
        *           <code>false</code> otherwise.
        */
       @Override
       public logical setAttribute(String name, character value)
       {
          return setAttribute(new character(name), value);
       }

       /**
        * Setting the attribute value for a given attribute name.
        *
        * @param    name
        *           The name of the attribute to change.
        * @param    value
        *           The new value for the given attribute name.
        *
        * @return   The <code>true</code> in case of success
        *           <code>false</code> otherwise.
        */
       @Override
       public logical setAttribute(character name, String value)
       {
          return setAttribute(name, new character(value));
       }

       /**
        * Setting the attribute value for a given attribute name.
        *
        * @param    name
        *           The name of the attribute to change.
        * @param    value
        *           The new value for the given attribute name.
        *
        * @return   The <code>true</code> in case of success <code>false</code> otherwise.
        */
       @Override
       public logical setAttribute(character name, character value)
       {
          if (!isInitialized())
          {
             invalidNodeError("SET-ATTRIBUTE");
             return new logical(false);
          }
          if (name.isUnknown() || value.isUnknown())
          {
             invalidArgumentError("SET-ATTRIBUTE");
             return new logical(false);
          }

          Node node = getNode();
          final logical result;
          if (node.getNodeType() == Node.ELEMENT_NODE)
          {
             Element elementNode = (Element) node;
             try
             {
                elementNode.setAttribute(name.toStringMessage(), value.toStringMessage());
             }
             catch (DOMException e)
             {
                if (e.code == DOMException.INVALID_CHARACTER_ERR)
                {
                   commonDomError("SET-ATTRIBUTE", "An invalid character was specified");
                }
                else if (e.code == DOMException.NO_MODIFICATION_ALLOWED_ERR)
                {
                   commonDomError("SET-ATTRIBUTE", "Read only node");
                }
                return new logical(false);
             }
             result = new logical(true);
          }
          else
          {
             commonDomError("SET-ATTRIBUTE", "Invalid node type");
             result = new logical(false);
          }

          return result;
       }

       /**
        * Associates XML attribute node with the given X-Noderef object handle.
        *
        * @param    handle
        *           The handle that represents an XML attribute node created with create node or
        *           create node namespace.
        *
        * @return   The <code>true</code> in case of success <code>false</code> otherwise.
        */
       @Override
       public logical setAttributeNode(handle handle)
       {
          if (handle._isValid() && !handle.isUnknown() && handle.get() instanceof HandleResource)
          {
             HandleResource resource = (HandleResource) handle.get();
             if (LegacyResource.X_DOCUMENT.equalsIgnoreCase(resource.type()))
             {
                invalidArgumentError("SET-ATTRIBUTE-NODE");
                return new logical(false);
             }
          }

          if (!isInitialized() || handle.isUnknown())
          {
             invalidNodeError("SET-ATTRIBUTE-NODE");
             return new logical(false);
          }

          if (!isXNodeHandle(handle))
          {
             invalidArgumentError("SET-ATTRIBUTE-NODE");
             return new logical(false);
          }
          Node currentNode = getNode();
          Object resource = handle.get();
          Node handleNode = ((XNodeRefImpl) resource).getNode();
          final logical result;
          if (currentNode.getNodeType() == Node.ELEMENT_NODE &&
              handleNode.getNodeType() == Node.ATTRIBUTE_NODE)
          {
             Element elementNode = (Element) currentNode;
             try
             {
                elementNode.setAttributeNode((Attr) handleNode);
             }
             catch (DOMException e)
             {
                if (e.code == DOMException.WRONG_DOCUMENT_ERR)
                {
                   invalidNodeError("SET-ATTRIBUTE-NODE");
                }
                else if (e.code == DOMException.INUSE_ATTRIBUTE_ERR)
                {
                   commonDomError("SET-ATTRIBUTE-NODE", "Attribute already exists");
                }
                else if (e.code == DOMException.NO_MODIFICATION_ALLOWED_ERR)
                {
                   commonDomError("SET-ATTRIBUTE-NODE",
                                  "An attempt was made to modify an object " +
                                  "where modifications are not allowed");
                }
                return new logical(false);
             }

             result = new logical(true);
          }
          else
          {
             commonDomError("SET-ATTRIBUTE-NODE", "Invalid node type");
             result = new logical(false);
          }

          return result;
       }

       /**
        * Associates XML attribute node with the specified name to the given X-Noderef object
        * handle.
        *
        * @param    handle
        *           The handle that represents an XML attribute node with the specified name.
        * @param    attributeName
        *           The string that represents an XML attribute node with the specified name.
        *
        * @return   The <code>true</code> in case of success <code>false</code> otherwise.
        */
       @Override
       public logical getAttributeNode(handle handle, String attributeName)
       {
          return getAttributeNode(handle, new character(attributeName));
       }

       /**
        * Associates XML attribute node with the specified name to the given X-Noderef object
        * handle.
        *
        * @param    handle
        *           The handle that represents an XML attribute node with the specified name.
        * @param    attributeName
        *           The character that represents an XML attribute node with the specified name.
        *
        * @return   The <code>true</code> in case of success <code>false</code> otherwise.
        */
       @Override
       public logical getAttributeNode(handle handle, character attributeName)
       {
          if (handle._isValid() && !handle.isUnknown() && handle.get() instanceof HandleResource)
          {
             HandleResource resource = (HandleResource) handle.get();
             if (LegacyResource.X_DOCUMENT.equalsIgnoreCase(resource.type()))
             {
                invalidArgumentError("GET-ATTRIBUTE-NODE");
                return new logical(false);
             }
          }

          if (!isXNodeHandle(handle) || attributeName.isUnknown())
          {
             invalidArgumentError("GET-ATTRIBUTE-NODE");
             return new logical(false);
          }

          if (!isInitialized())
          {
             invalidNodeError("GET-ATTRIBUTE-NODE");
             return new logical(false);
          }

          Node node = getNode();
          final logical result;
          if (node.getNodeType() == Node.ELEMENT_NODE)
          {
             Element elementNode = (Element) node;
             Attr attrNode = elementNode.getAttributeNode(attributeName.toStringMessage());
             if (attrNode != null)
             {
                setNodeToHandle(handle, attrNode);
             }
             result = handle.isValid();
          }
          else
          {
             commonDomError("GET-ATTRIBUTE-NODE", "Invalid node type");
             result = new logical(false);
          }
          return result;
       }

       /**
        * Setting the binary value of the XML node value attribute.
        *
        * @param    value
        *           The new value of the node value attribute.
        *
        * @return   The <code>true</code> in case of success <code>false</code> otherwise.
        */
       @Override
       public logical longcharToNodeValue(longchar value)
       {
          if (!isInitialized())
          {
             invalidNodeError("LONGCHAR-TO-NODE-VALUE");
             return new logical(false);
          }

          if (value == null || value.isUnknown())
          {
             invalidArgumentError("LONGCHAR-TO-NODE-VALUE");
             return new logical(false);
          }

          Node node = getNode();
          node.setTextContent(value.toStringMessage());

          return new logical(true);
       }

       /**
        * Setting the XML node value attribute to binary value of longchar.
        *
        * @param    value
        *           To save the value on XML node to this parameter.
        *
        * @return   The <code>true</code> in case of success <code>false</code> otherwise.
        */
       @Override
       public logical nodeValueToLongchar(longchar value)
       {
          if (!isInitialized())
          {
             invalidNodeError("NODE-VALUE-TO-LONGCHAR");
             return new logical(false);
          }

          value.setValue(getNode().getNodeValue());

          return new logical(true);
       }

       /**
        * Setting the binary value of the XML node value attribute.
        *
        * @param    value
        *           The new value of the node value attribute.
        *
        * @return   The <code>true</code> in case of success <code>false</code> otherwise.
        */
       @Override
       public logical memptrToNodeValue(memptr value)
       {
          if (!isInitialized())
          {
             invalidNodeError("MEMPTR-TO-NODE-VALUE");
             return new logical(false);
          }

          if (value == null || value.isUnknown())
          {
             invalidArgumentError("MEMPTR-TO-NODE-VALUE");
          }

          Node node = getNode();
          try
          {
             node.setTextContent(new String(value.getByteArray()));
          }
          catch (DOMException e)
          {
             if (e.code == DOMException.NO_MODIFICATION_ALLOWED_ERR)
             {
                commonDomError("MEMPTR-TO-NODE-VALUE", "Read only node");//TODO need correct message
                return new logical(false);
             }
          }
          return new logical(true);
       }

       /**
        * Setting the XML node value attribute to binary value of memptr.
        *
        * @param    value
        *           To save the value on XML node to this parameter.
        *
        * @return   The <code>true</code> in case of success <code>false</code> otherwise.
        */
       @Override
       public logical nodeValueToMemptr(memptr value)
       {
          if (!isInitialized())
          {
             invalidNodeError("NODE-VALUE-TO-MEMPTR");
             return logical.FALSE;
          }

          value.setString(getNode().getNodeValue(), 1);

          return logical.TRUE;
       }

       /**
        * Normalizes TEXT and ATTRIBUTE nodes in the full depth of the sub-tree under this
        * XML node.
        *
        * @return   The <code>true</code> in case of success <code>false</code> otherwise.
        */
       @Override
       public logical normalize()
       {
          if (!isInitialized())
          {
             invalidNodeError("NORMALIZE");
             return logical.FALSE;
          }

          Node node = getNode();
          short nodeType = node.getNodeType();
          if (nodeType == Node.ELEMENT_NODE)
          {
             node.normalize();
          }
          else
          {
             commonDomError("NORMALIZE", "Invalid node type");
             return logical.FALSE;
          }

          return logical.TRUE;
       }

       /**
        * Get the value of the LOCAL-NAME attribute.
        *
        * @return   See above.
        */
       @Override
       public character getLocalName()
       {
          if (!isInitialized())
          {
             invalidNodeError("LOCAL-NAME");
             return character.UNKNOWN;
          }

          Node node = getNode();
          String localName = node.getLocalName();
          if (localName != null)
          {
             return new character(localName);
          }
          else
          {
             // otherwise, always return empty
             return character.EMPTY_STRING;
          }
       }

       /**
        * Setter for NAMESPACE-PREFIX attribute.
        *
        * @param    prefix
        *           The new value of the NAMESPACE-PREFIX attribute.
        */
       @Override
       public void namespacePrefix(character prefix)
       {
          if (!isInitialized())
          {
             String[] errMsg =
             {
                "X-NODEREF must be associated with a valid X-DOCUMENT in order " +
                   "to use it in method NAMESPACE-PREFIX",
                "Unable to set attribute NAMESPACE-PREFIX in widget  of type X-NODEREF"
             };
             int[] errorCodes = { 9102, 3131 };
             ErrorManager.recordOrShowError(errorCodes, errMsg, false, false, true);
             return;
          }

          if (prefix.isUnknown())
          {
             invalidArgumentAssignError("NAMESPACE-PREFIX");
             return;
          }

          Node currentNode = getNode();
          String prefixStr = prefix.getValue(); //null is ok here
          try
          {
             if (currentNode.getNodeType() == Node.ELEMENT_NODE)
             {
                Element elementNode = (Element) currentNode;
                elementNode.setPrefix(prefixStr);
             }
             else if (currentNode.getNodeType() == Node.ATTRIBUTE_NODE)
             {
                Attr attributeNode = (Attr) currentNode;
                attributeNode.setPrefix(prefixStr);
             }
          }
          catch (DOMException e)
          {
             if (e.code == DOMException.NO_MODIFICATION_ALLOWED_ERR)
             {
                commonDomError("NAMESPACE-PREFIX",
                               "An attempt was made to modify an object where modifications are not allowed");
             }
             else if (e.code == DOMException.NAMESPACE_ERR ||
                      e.code == DOMException.INVALID_CHARACTER_ERR)
             {
                String[] errMsg =
                {
                   "X-NODEREF or X-DOCUMENT NAMESPACE-PREFIX got an error: XML Parser not initialized",
                   "Unable to set attribute NAMESPACE-PREFIX in widget  of type X-NODEREF"
                };
                int[] errorCodes = { 9082, 3131 };
                ErrorManager.recordOrShowError(errorCodes, errMsg, false, false, true);
             }
          }
       }

       /**
        * Gets the number of child for the current one. Only for XNodRef
        *
        * @return   The integer number of the child, 1-based index of the child node.
        */
       @Override
       public integer getChildNum()
       {
          if (!isInitialized())
          {
             invalidNodeError("NUM-CHILDREN");
             return new integer();
          }

          if (LegacyResource.X_DOCUMENT.equalsIgnoreCase(type()))
          {
             return new integer();
          }

          Node node = getNode();
          Node parent = node.getParentNode();
          if (parent == null)
          {
             return new integer(1);
          }

          NodeList childNodes = parent.getChildNodes();
          for (int i = 0 ; i < childNodes.getLength(); i++)
          {
             if (node.equals(childNodes.item(i)))
             {
                return new integer(i + 1);
             }
          }

          return new integer();
       }

       /**
        * Getting the Document the current reference belongs to.
        *
        * @return   The handle of the Document containing tne current reference.
        */
       @Override
       public handle getOwnerDocument()
       {
          if (!isInitialized())
          {
             invalidNodeError("OWNER-DOCUMENT");
             return new handle();
          }

          Document doc = getNode().getOwnerDocument();

          // check if access to the owner document is restricted
          Object restricted = doc.getUserData(RESTRICTED_DOCUMENT);
          if (restricted != null && (restricted instanceof Boolean) && ((Boolean) restricted))
          {
             final String msg = "X-NODEREF must be associated with a valid X-DOCUMENT in order to " +
                                "use it in method OWNER-DOCUMENT";
             ErrorManager.recordOrShowError(9102, msg, false, false, false);

             return new handle();
          }

          return new handle(new XDocumentImpl(doc));
       }
    }