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));
   }
}