# XEntityImpl.java

    /*
    ** Module   : XEntityImpl
    ** Abstract : DOM and SAX common 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 EVL 20130214 Adding the base inheritance from XCommonImpl class to place the XML related
    **                  common features.
    ** 003 CA  20130221 Added UNIQUE-ID and NAMESPACE-URI support.
    ** 004 EVL 20130222 Adding SUBTYPE attribute and INSERT-BEFORE method support.
    ** 005 OM  20130304 Refactored isValid and isUnknown of WrappedResource to valid and unknown.
    ** 006 OM  20130320 Fixed getChild() method, added Override annotations, cosmetics & typo fixes.
    ** 007 EVK 20130527 Adding support CHILD-NUM, NAMESPACE-PREFIX attributes and REPLACE-CHILD
    **                  method.
    **                  Add implementation for SUBTYPE, NUM-CHILDREN, CHILD-NUM, UNIQUE-ID,
    **                  NAMESPACE-URI, NAME, NAMESPACE-PREFIX attributes and IMPORT-NODE,
    **                  APPEND-CHILD, INSERT-BEFORE,REMOVE-CHILD, REPLACE-CHILD, GET-CHILD,
    **                  REMOVE-ATTRIBUTE methods.
    **                  Remove map Element->handle, because we don't need to store handle
    **                  reference for children.
    **                  xnode variable is now Node instead of Element.
    ** 008 CA  20130927 Removed unknown() API, as by default the resource is known.
    ** 009 SVL 20131203 Changes caused by renaming of Nameable.get/setName to Nameable.name.
    ** 010 HC  20131215 Implemented UNIQUE-ID attribute.
    ** 011 EVK 20131216 Moved up to hierarchy next methods:
    **                  invalidArgumentError, invalidArgumentAssignError.
    ** 012 EVK 20140113 Removed method valid(moved to XCommonImpl class) and implemented resourceDelete
    **                  method to properly remove the object.
    ** 013 CA  20140106 Made setNode public.
    ** 014 OM  20201120 Renamed attribute accessors to fit buffer attributes naming convention.
    **     EVL 20210218 Changed integer to int64 to match MathOps changes.
    **     OM  20210309 Removed deprecation warnings.
    **     ME  20210325 Throw error instead of show for invalid xml.
    **     CA  20211227 GetChild must show an error if the index is not a valid child, and not raise an ERROR
    **                  condition.
    **     CA  20221006 UNIQUE-ID is kept as Java type instead of BDT.  Refs #6827
    **     CA  20230116 Avoid using handle.unwrap, handle.getReference or other BDT usage from within FWD runtime.
    ** 015 CA  20230720 The NAMESPACE-URI setter with an unknown value must raise error 4083.
    ** 016 ICP 20250123 Used integer and logical constants to leverage caches 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 common features for DOM.
     */
    public abstract class XEntityImpl
    extends XCommonImpl
    implements XEntity
    {
       /** Constant to specify uninitialized state for node if owner of this node has been deleted. */
       static final String IS_INVALID_NODE = "IS_INVALID_NODE";

       /** Element associates with current XEntity class. */
       private Node xnode = null;

       /** Corresponds to UNIQUE-ID attribute. */
       private Long uniqueID;

       /** The kind of this entity. */
       private IdKind idKind;

       /**
        * Default constructor.
        *
        * @param    idKind
        *           The discriminator for unique id allocation.
        */
       protected XEntityImpl(IdKind idKind)
       {
          this.idKind = idKind;
       }

       /**
        * Special constructor accepting a X-Document or X-Node-Reference object.
        *
        * @param    node
        *           The DOM XML node object to be associated with the given
        *           handle object.
        *
        * @param    idKind
        *           The discriminator for unique id allocation.
        */
       protected XEntityImpl(Node node, IdKind idKind)
       {
          this(idKind);
          xnode = node;
       }

       /**
        * Utility method to set up node value into handle. If handle is not XNodeRef or XDocument
        * type then do nothing.
        *
        * @param    handle
        *           The handle class instance to set new node value.
        * @param    node
        *           The node value to set.
        */
       static void setNodeToHandle(handle handle, Node node)
       {
          if (isXEntityHandle(handle))
          {
             ((XEntityImpl)handle.getResource()).setNode(node);
          }
       }

       /**
        * Check the type of resource in handle. Return <code>true</code> if and only if handle
        * contains resource of type XNodeRef.
        *
        * @param    handle
        *           The handle class instance to check type.
        *
        * @return   See above.
        */
       static boolean isXNodeHandle(handle handle)
       {
          if (handle == null || !handle._isValid() || handle.isUnknown())
          {
             return false;
          }
          WrappedResource resource = handle.getResource();
          return resource instanceof XNodeRefImpl;
       }

       /**
        * Check the type of resource in handle. Return <code>true</code> if and only if handle
        * contains resource of type XNodeRef or XDocument.
        *
        * @param    handle
        *           The handle class instance to check type.
        *
        * @return   See above.
        */
       static boolean isXEntityHandle(handle handle)
       {
          if (handle == null || !handle._isValid() || handle.isUnknown())
          {
             return false;
          }
          WrappedResource resource = handle.getResource();
          return resource instanceof XEntityImpl;
       }

       /**
        * Check the type of resource in handle. Return <code>true</code> if and only if handle
        * contains resource of type XNodeRef.
        *
        * @param    handle
        *           The handle class instance to check type.
        *
        * @return   See above.
        */
       static boolean isXNodeHandleInitialized(handle handle)
       {
          if (!isXNodeHandle(handle))
          {
             return false;
          }
          WrappedResource resource = handle.getResource();
          return ((XEntityImpl) resource).isInitialized();
       }

       /**
        * Getting the Element backend object for this XEntity.
        *
        * @return   The Element associated with object.
        */
       public Node getNode()
       {
          return xnode;
       }

       /**
        * Delete the resource.
        *
        * @return   <code>true</code> if the resource was deleted.
        */
       @Override
       protected boolean resourceDelete()
       {
          xnode = null;
          isValid = false;
          return true;
       }

       /**
        * Gets the the subtype of the object.
        *
        * @return   The character value of the subtype attribute.
        */
       @Override
       public character getSubType()
       {
          if (!isInitialized())
          {
             invalidNodeError("SUBTYPE");
             return new character();
          }
          Node node = getNode();
          return new character(XmlFactory.convertNodeToAblNode(node.getNodeType()));
       }

       /**
        * Sets the the subtype of the object.
        *
        * @param    value
        *           The new value of the subtype object attribute.
        */
       @Override
       public void setSubType(character value)
       {
          readOnlyError("SUBTYPE", handle.UNKNOWN_ARGUMENT);
       }

       /**
        * Sets the the subtype of the object.
        *
        * @param    value
        *           The new value of the subtype object attribute.
        */
       @Override
       public void setSubType(String value)
       {
          setSubType(new character(value));
       }

       /**
        * Inserts a node as a child of this document before another node or last if other node is
        * unknown.
        *
        * @param    handle1
        *           The handle representing the child to insert.
        * @param    handle2
        *           The handle representing the XML node to be inserted before.
        *
        * @return   The <code>true</code> in case of success <code>false</code> otherwise.
        */
       @Override
       public logical insertBefore(handle handle1, handle handle2)
       {
          if (!isInitialized())
          {
             invalidNodeError("INSERT-BEFORE");
             return new logical(false);
          }

          if (!isXNodeHandle(handle1) || !handle2.isUnknown() && !isXNodeHandle(handle2))
          {
             invalidArgumentError("INSERT-BEFORE");
             return new logical(false);
          }

          if (handle1.isUnknown() || !handle2.isUnknown() && !isXNodeHandleInitialized(handle2))
          {
             invalidNodeError("INSERT-BEFORE");
             return new logical(false);
          }

          if (handle2.isUnknown())
          {
             return appendChild(handle1);
          }
          Node node1 = ((XEntityImpl) handle1.get()).getNode();
          Node node2 = ((XEntityImpl) handle2.get()).getNode();
          try
          {
             getNode().insertBefore(node1, node2);
          }
          catch (DOMException e)
          {
             if (e.code == DOMException.WRONG_DOCUMENT_ERR)
             {
                commonDomError("INSERT-BEFORE",
                               "A node was used in a different document than the one the created it");
             }
             else if (e.code == DOMException.HIERARCHY_REQUEST_ERR)
             {
                commonDomError("INSERT-BEFORE",
                               "An attempt was made to reference a node in a context where it does not exist");
             }
             else if (e.code == DOMException.NOT_FOUND_ERR)
             {
                commonDomError("INSERT-BEFORE", "A node was inserted where it doesn't belong");
             }
             return new logical(false);
          }

          return new logical(true);
       }

       /**
        * Gets the the number of children objects for the current one.
        *
        * @return   The integer number of the children.
        */
       @Override
       public integer getNumChildren()
       {
          if (!isInitialized())
          {
             invalidNodeError("NUM-CHILDREN");
             return integer.UNKNOWN;
          }

          return integer.of(getNode().getChildNodes().getLength());
       }

       /**
        * Removing the specified child from the node.
        *
        * @param    newChildHandle
        *           The handle class instance that represents the node to insert in the tree.
        * @param    oldChildHandle
        *           The handle class instance that represents the node to remove from the tree.
        *
        * @return   The <code>true</code> in case of success <code>false</code> otherwise.
        */
       @Override
       public logical replaceChild(handle newChildHandle, handle oldChildHandle)
       {
          if (!isInitialized())
          {
             invalidNodeError("REPLACE-CHILD");
             return new logical(false);
          }
          if (!newChildHandle._isValid())
          {
             invalidArgumentError("REPLACE-CHILD");
             return new logical(false);
          }
          if (newChildHandle.isUnknown())
          {
             invalidNodeError("REPLACE-CHILD");
             return new logical(false);
          }
          if (!oldChildHandle._isValid())
          {
             invalidArgumentError("REPLACE-CHILD");
             return new logical(false);
          }
          if (oldChildHandle.isUnknown())
          {
             invalidNodeError("REPLACE-CHILD");
             return new logical(false);
          }

          Node newChild = ((XEntityImpl) newChildHandle.get()).getNode();
          Node oldChild = ((XEntityImpl) oldChildHandle.get()).getNode();
          try
          {
             getNode().replaceChild(newChild, oldChild);
          }
          catch (DOMException e)
          {
             if (e.code == DOMException.WRONG_DOCUMENT_ERR)
             {
                commonDomError("REPLACE-CHILD",
                               "A node was used in a different document than the one " +
                               "the created it");
             }
             else if (e.code == DOMException.HIERARCHY_REQUEST_ERR)
             {
                commonDomError("REPLACE-CHILD", "A node was inserted where doesn't belong");
             }
             else if (e.code == DOMException.NOT_FOUND_ERR)
             {
                commonDomError("REPLACE-CHILD",
                               "An attempt was made to reference a node in a context where " +
                               "it does not exist");
             }

             return new logical(false);
          }

          return new logical(true);
       }

       /**
        * Appending the new child to the current object.
        *
        * @param    hChild
        *           The valid handle class instance of the child to be appended.
        *
        * @return   The <code>true</code> in case of success <code>false</code> otherwise.
        */
       @Override
       public logical appendChild(handle hChild)
       {
          if (!isInitialized() || !valid())
          {
             return new logical(false);
          }

          if (!isXNodeHandle(hChild))
          {
             invalidArgumentError("APPEND-CHILD");
             return new logical(false);
          }

          if (!isXNodeHandleInitialized(hChild))
          {
             invalidNodeError("APPEND-CHILD");
             return new logical(false);
          }

          Node node = getNode();
          Node childNode = ((XEntityImpl) hChild.get()).getNode();
          try
          {
              node.appendChild(childNode);
          }
          catch (DOMException e)
          {
             if (e.code == DOMException.WRONG_DOCUMENT_ERR)
             {
                commonDomError("APPEND-CHILD",
                               "A node was used in a different document than the one the " +
                               "created it");
             }
             else if (e.code == DOMException.HIERARCHY_REQUEST_ERR)
             {
                commonDomError("APPEND-CHILD", "A node was inserted where it doesn't belong");
             }
             else if (e.code == DOMException.NO_MODIFICATION_ALLOWED_ERR)
             {
                commonDomError("APPEND-CHILD",
                               "An attempt was made to modify an object where modifications " +
                               "are not allowed");
             }
             return new logical(false);
          }
          return new logical(true);
       }

       /**
        * Getting the handle object of the child node with specified index.
        *
        * @param    hChild
        *           The valid handle class instance of the child to put
        *           the results.
        * @param    index
        *           1-based index of the child node to be retrieved.
        *
        * @return   The <code>true</code> in case of success
        *           <code>false</code> otherwise.
        */
       @Override
       public logical getChild(handle hChild, int index)
       {
          return getChild(hChild, new int64(index));
       }

       /**
        * Getting the handle object of the child node with specified index.
        *
        * @param    hChild
        *           The valid handle class instance of the child to put the results.
        * @param    index
        *           1-based index of the child node to be retrieved.
        *
        * @return   The <code>true</code> in case of success <code>false</code> otherwise.
        */
       @Override
       public logical getChild(handle hChild, int64 index)
       {
          if (!isInitialized() || !isXNodeHandle(hChild))
          {
             invalidNodeError("GET-CHILD");
             return logical.FALSE;
          }

          if (!hChild._isValid() || index.isUnknown())
          {
             invalidArgumentError("GET-CHILD");
             return logical.FALSE;
          }

          int ndx = index.intValue();

          NodeList childNodes = getNode().getChildNodes();

          if (ndx < 1 || ndx > childNodes.getLength())
          {
             commonDomError("GET-CHILD", "Invalid child index", false);
             return logical.FALSE;
          }

          Node node = childNodes.item(ndx - 1);

          setNodeToHandle(hChild, node);

          return logical.TRUE;
       }

       /**
        * Removing the specified child from the node.
        *
        * @param    hChild
        *           The valid handle class instance of the child to be removed.
        *
        * @return   The <code>true</code> in case of success
        *           <code>false</code> otherwise.
        */
       @Override
       public logical removeChild(handle hChild)
       {
          if (!isInitialized())
          {
             invalidNodeError("REMOVE-CHILD");
             return logical.FALSE;
          }

          if (!hChild._isValid())
          {
             invalidArgumentError("REMOVE-CHILD");
             return logical.FALSE;
          }

          Node elChild = ((XEntityImpl) hChild.get()).getNode();
          try
          {
             getNode().removeChild(elChild);
          }
          catch (DOMException e)
          {
             if (e.code == DOMException.NOT_FOUND_ERR)
             {
                commonDomError("REMOVE-CHILD",
                               "An attempt was made to reference a node in a context where it " +
                               "does not exist");
             }
             else if (e.code == DOMException.NO_MODIFICATION_ALLOWED_ERR)
             {
                commonDomError("REMOVE-CHILD",
                               "An attempt was made to modify an object where modifications " +
                               "are not allowed");
             }
             return logical.FALSE;
          }
          return logical.TRUE;
       }

       /**
        * Gets the the unique ID number associated to this object by the underlying system. Not the
        * same as the handle value itself. For X-noderef it's only unique for current xml Document.
        *
        * @return   The integer unique number of node inside document.
        */
       @Override
       public integer getUniqueID()
       {
          if (uniqueID == null)
          {
             // Although the reference says "The UNIQUE-ID attribute for an x-noderef handle is only
             // unique within a given XML document.", the implementation extends the uniqueness up
             // to the current user context.
             this.uniqueID = UniqueIdGenerator.getUniqueId(idKind);
          }
          return new integer(this.uniqueID);
       }

       /**
        * Returns the value of the the NAMESPACE-URI attribute.
        *
        * @return    See above.
        */
       @Override
       public character namespaceURI()
       {
          if (!isInitialized())
          {
             invalidNodeError("NAMESPACE-URI");
             return new character();
          }

          String namespaceUri = getNode().getNamespaceURI();
          if (namespaceUri != null)
          {
             return new character(namespaceUri);
          }
          else
          {
             return new character("");
          }
       }

       /**
        * Setter for NAMESPACE-URI attribute. Calls {@link #readOnlyError}, as this attribute is r/o
        * for this resource.
        *
        * @param    uri
        *           Not used.
        */
       @Override
       public void namespaceURI(String uri)
       {
          readOnlyError("NAMESPACE-URI", handle.UNKNOWN_ARGUMENT);
       }

       /**
        * Setter for NAMESPACE-URI attribute. Calls {@link #readOnlyError}, as this attribute is r/o
        * for this resource.
        *
        * @param    uri
        *           Not used.
        */
       @Override
       public void namespaceURI(character uri)
       {
          if (uri.isUnknown())
          {
             invalidArgumentAssignError("NAMESPACE-URI");
             return;
          }
          readOnlyError("NAMESPACE-URI", handle.UNKNOWN_ARGUMENT);
       }

       /**
        * Removes the specified attribute.
        *
        * @param    name
        *           The name of the attribute to remove.
        * @param    uri
        *           The URI of the attribute to remove.
        *
        * @return   The <code>true</code> in case of success <code>false</code> otherwise.
        */
       @Override
       public logical removeAttribute(character name, character uri)
       {
          if (name.isUnknown())
          {
             invalidArgumentError("REMOVE-ATTRIBUTE");
             return logical.FALSE;
          }

          if (!isInitialized())
          {
             invalidNodeError("REMOVE-ATTRIBUTE");
             return logical.FALSE;
          }

          Node node = getNode();
          if (node.getNodeType() != Node.ELEMENT_NODE)
          {
             commonDomError("REMOVE-ATTRIBUTE", "Invalid node type");
             return logical.FALSE;
          }

          Element elementNode = (Element) node;
          String nameStr = name.toStringMessage();
          if (uri.isUnknown())
          {
             elementNode.removeAttribute(nameStr);
          }
          else
          {
             elementNode.removeAttributeNS(uri.toStringMessage(), nameStr);
          }

          return logical.TRUE;
       }

       /**
        * Removes the specified attribute. This method is not applicable for Dom.
        *
        * @param    index
        *           The 1-based index of the attribute to remove.
        * @param    uri
        *           The URI of the attribute to remove.
        *
        * @return   The <code>true</code> in case of success <code>false</code> otherwise.
        */
       @Override
       public logical removeAttribute(NumberType index, character uri)
       {
          return new logical(false); //only for SAX
       }

       /**
        * Setter for NAMESPACE-PREFIX attribute.
        *
        * @param    prefix
        *           The new value of the NAMESPACE-PREFIX attribute.
        */
       @Override
       public void namespacePrefix(String prefix)
       {
          namespacePrefix(new character(prefix));
       }

       /**
        * Setter for NAMESPACE-PREFIX attribute.
        *
        * @param    prefix
        *           The new value of the NAMESPACE-PREFIX attribute.
        */
       @Override
       public void namespacePrefix(character prefix)
       {
          readOnlyError("NAMESPACE-PREFIX", handle.UNKNOWN_ARGUMENT);
       }

       /**
        * Returns the value of the the NAMESPACE-PREFIX attribute. Only for ELEMENT or ATTRIBUTE,
        * for other attributes return unknown value.
        *
        * @return    See above.
        */
       @Override
       public character namespacePrefix()
       {
          if (!isInitialized() || LegacyResource.X_DOCUMENT.equalsIgnoreCase(type()))
          {
             invalidNodeError("NAMESPACE-PREFIX");
             return new character();
          }

          Node currentNode = getNode();
          final String prefix;
          if (currentNode.getNodeType() == Node.ELEMENT_NODE)
          {
             Element elementNode = (Element) currentNode;
             prefix = elementNode.getPrefix();
          }
          else if (currentNode.getNodeType() == Node.ATTRIBUTE_NODE)
          {
             Attr attributeNode = (Attr) currentNode;
             prefix = attributeNode.getPrefix();
          }
          else
          {
             prefix = null;
          }

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

       /**
        * Get the <code>name</code> attribute of handle.
        *
        * @return   See above.
        */
       @Override
       public character name()
       {
          if (!isInitialized())
          {
             invalidNodeError("NAME");
             return new character();
          }

          Node node = getNode();

          return new character(node.getNodeName());
       }

       /**
        * Get the <code>name</code> attribute of handle as a string.
        *
        * @return See above.
        */
       @Override
       public String _name()
       {
          return name().toJavaType();
       }

       /**
        * Setting the Element backend object for this XEntity.
        *
        * @param    xnode
        *           The node value to set.
        */
       public void setNode(Node xnode)
       {
          this.xnode = xnode;
       }

       /**
        * Process and display invalid node error. It displays appropriate error message.
        *
        * @param    attribute
        *           The read-only attribute.
        */
       protected void invalidNodeError(String attribute)
       {
          if (attribute == null)
          {
             throw new IllegalArgumentException("The attribute is null !");
          }

          String msg = "X-NODEREF must be associated with a valid X-DOCUMENT in order " +
                       "to use it in method %s";
          String err = String.format(msg, attribute.toUpperCase());
          ErrorManager.recordOrShowError(9102, err, false, false, false);
       }

       /**
        * Process and display unknown node error. It displays appropriate error message.
        *
        * @param    attribute
        *           The read-only attribute.
        * @param    message
        *           Error message to display.
        */
       protected void commonDomError(String attribute, String message)
       {
          commonDomError(attribute, message, true);
       }

       /**
        * Process and display unknown node error. It displays appropriate error message.
        *
        * @param    attribute
        *           The read-only attribute.
        * @param    message
        *           Error message to display.
        * @param    raiseError
        *           Flag indicating if an ERROR condition must be raised.
        */
       protected void commonDomError(String attribute, String message, boolean raiseError)
       {
          if (attribute == null)
          {
             throw new IllegalArgumentException("The attribute is null !");
          }

          String msg = "X-NODEREF or X-DOCUMENT %s got an error: %s";
          String err = String.format(msg, attribute.toUpperCase(), message);
          if (raiseError)
          {
             ErrorManager.recordOrThrowError(9082, err, false, false);
          }
          else
          {
             ErrorManager.recordOrShowError(9802, err, true, false, false);
          }
       }

       /**
        * Process and display unsupported xml sub-type error. It displays appropriate error message.
        *
        * @param    type
        *           The read-only attribute.
        */
       protected void unsupportedDomError(String type)
       {
          String msg = "Unsupported XML SUB-TYPE %s";
          String err = String.format(msg, type);
          ErrorManager.recordOrShowError(9171, err, false, true, false);
       }

       /**
        * Reports if this object is not initialized. Initialized means that object is created but
        * doesn't have any xml node. If the owner of node has been already deleted then node becomes
        * uninitialized.
        *
        * @return   <code>true</code> if object is initialized.
        */
       boolean isInitialized()
       {
          Node node = getNode();
          if (node == null)
          {
             return false;
          }

          if (node.getOwnerDocument() == null)
          {
             return true;
          }

          Object data = node.getOwnerDocument().getUserData(IS_INVALID_NODE);
          if (data != null && Boolean.valueOf(data.toString()))
          {
             xnode = null;
             return false;
          }
          return true;
       }
    }