# XmlFactory.java

    /*
    ** Module   : XmlFactory
    ** Abstract : DOM and SAX XML instance creation.
    **
    ** Copyright (c) 2013-2023, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- --------------------------------Description-----------------------------------
    ** 001 EVL 20130204 Created initial version. Major rework to use w3c backend.
    ** 002 EVL 20130208 Added SAX XML Support.
    ** 003 CA  20130308 The CREATE APIs need to receive a handle parameter, to properly initiate it,
    **                  as returning the new handle instance and changing the handle reference in the
    **                  caller is not the right approach.
    ** 004 CA  20130309 Added missing APIs related to WIDGET-POOL clause.
    ** 005 CA  20130311 Added more missing APIs related to WIDGET-POOL clause.
    ** 006 CA  20130918 Added widget-pool support. The pool name must be validated before anything
    **                  else, and a null name means the default pool is used.
    ** 007 EVK 20130531 Added maps ABL node name to Dom nodeId and Dom nodeId to
    **                  ABL node name. Added methods convertNodeToAblNode() and
    **                  convertAblNodeToNodeId().
    **                  createXNodeReference() methods now uses handle in parameters instead of
    **                  Document interface.
    **                  Added type support for createXNodeReference() method.
    ** 008 CA  20130927 Create all resources, even if they are not fully-implemented.
    ** 009 CA  20230724 Further reduce context-local usage.
    */
    /*
    ** 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 com.goldencode.p2j.util.*;

    import org.w3c.dom.*;

    import java.util.*;

    import static com.goldencode.p2j.xml.XmlTokenTypes.*;

    /**
     * The set of methods to work with DOM XML and SAX support implementation in P2J.
     */
    public class XmlFactory
    {
       /** Map of token names to token types */
       private static final Map<String, Integer> nodeNameToNodeId = new HashMap<String, Integer>();

       /** Map of token types to token names */
       private static final Map<Integer, String> nodeIdToNodeName = new HashMap<Integer, String>();

       // Initialize token maps.
       static
       {
          nodeNameToNodeId.put("ATTRIBUTE", ATTRIBUTE_NODE);
          nodeNameToNodeId.put("CDATA-SECTION", CDATA_SECTION_NODE);
          nodeNameToNodeId.put("COMMENT", COMMENT_NODE);
          nodeNameToNodeId.put("DOCUMENT", DOCUMENT_NODE);
          nodeNameToNodeId.put("DOCUMENT-FRAGMENT", DOCUMENT_FRAGMENT_NODE);
          nodeNameToNodeId.put("ELEMENT", ELEMENT_NODE);
          nodeNameToNodeId.put("ENTITY-REFERENCE", ENTITY_REFERENCE_NODE);
          nodeNameToNodeId.put("PROCESSING-INSTRUCTION", PROCESSING_INSTRUCTION_NODE);
          nodeNameToNodeId.put("TEXT", TEXT_NODE);
          for (Map.Entry<String, Integer> entry : nodeNameToNodeId.entrySet())
          {
             nodeIdToNodeName.put(entry.getValue(), entry.getKey());
          }
       }

       /**
        * Constructs a new instance of the Progress X-Document object and
        * retrieves the handle variable associated with it.
        *
        * @param    h
        *           The handle where the new resource will be set as the referent.
        */
       public static void createXDocument(handle h)
       {
          createXDocument(h, (character) null);
       }

       /**
        * Constructs a new instance of the Progress X-Document object and
        * retrieves the handle variable associated with it.
        *
        * @param    h
        *           The handle where the new resource will be set as the referent.
        * @param    poolName
        *           The name of the widget pool. <code>null</code> if the unnamed pool must be used.
        */
       public static void createXDocument(handle h, String poolName)
       {
          createXDocument(h, (poolName == null ? null : new character(poolName)));
       }

       /**
        * Constructs a new instance of the Progress X-Document object and
        * retrieves the handle variable associated with it.
        *
        * @param    h
        *           The handle where the new resource will be set as the referent.
        * @param    poolName
        *           The name of the widget pool. <code>null</code> if the unnamed pool must be used.
        */
       public static void createXDocument(handle h, character poolName)
       {
          // validate widget pool before anything else!!!
          if (!WidgetPool.validWidgetPool(poolName))
          {
             return;
          }

          XDocumentImpl res = new XDocumentImpl();
          h.assign(res);

          res.addToPool(poolName);
       }

       /**
        * Constructs a new instance of the Progress X-Node-Reference object and
        * retrieves the handle variable associated with it.
        *
        * @param    h
        *           The handle where the new resource will be set as the referent.
        */
       public static void createXNodeReference(handle h)
       {
          createXNodeReference(h, (character) null);
       }

       /**
        * Constructs a new instance of the Progress X-Node-Reference object and
        * retrieves the handle variable associated with it.
        *
        * @param    h
        *           The handle where the new resource will be set as the referent.
        * @param    poolName
        *           The name of the widget pool. <code>null</code> if the unnamed pool must be used.
        */
       public static void createXNodeReference(handle h, String poolName)
       {
          createXNodeReference(h, (poolName == null ? null : new character(poolName)));
       }

       /**
        * Constructs a new instance of the Progress X-Node-Reference object and
        * retrieves the handle variable associated with it.
        *
        * @param    h
        *           The handle where the new resource will be set as the referent.
        * @param    poolName
        *           The name of the widget pool. <code>null</code> if the unnamed pool must be used.
        */
       public static void createXNodeReference(handle h, character poolName)
       {
          // validate widget pool before anything else!!!
          if (!WidgetPool.validWidgetPool(poolName))
          {
             return;
          }

          XNodeRefImpl res = new XNodeRefImpl();
          h.assign(res);

          res.addToPool(poolName);
       }

       /**
        * Constructs a new instance of the Progress X-Node-Reference object and
        * retrieves the handle variable associated with it. The approach is
        * slightly different. Each node will have additional parameter - Name
        * Space and node will be addressed by namespace and name pair instead
        * of the simple name.
        *
        * @param    h
        *           The handle where the new resource will be set as the referent.
        *           The handle should contains resource with type of XNodeRef.
        * @param    docHandle
        *           handle that should contains the X-Document object to create X-Node under.
        * @param    nsUri
        *           Name space associated with the current set of names.
        * @param    name
        *           The name of the node to create.
        * @param    type
        *           The type of the node to create.
        */
       public static void createXNodeReference(handle    h,
                                               handle    docHandle,
                                               character nsUri,
                                               character name,
                                               character type)
       {
          if (docHandle.isUnknown() || !docHandle._isValid() || !XEntityImpl.isXNodeHandle(h))
          {
             return;
          }

          Document doc = getDocument(docHandle);
          if (doc == null)
          {
             return;
          }
          String nameImpl = null;
          String nsUriImpl = null;

          if (!nsUri.isUnknown())
          {
             nsUriImpl = nsUri.toStringMessage();
          }
          if (!name.isUnknown())
          {
             nameImpl = name.toStringMessage();
          }
          Integer nodeType = type.isUnknown() ?
                             ELEMENT_NODE :
                             convertAblNodeToNodeId(type.toStringMessage());

          final Node node;

          if (nodeType == ELEMENT_NODE)
          {
             node = doc.createElementNS(nsUriImpl, nameImpl);
          }
          else if (nodeType == ATTRIBUTE_NODE)
          {
             node = doc.createAttributeNS(nsUriImpl, nameImpl);
          }
          else
          {
             throw new DOMException(DOMException.TYPE_MISMATCH_ERR, "Unknown type");
          }

          ((XEntityImpl)h.getResource()).setNode(node);
       }

       /**
        * Constructs a new instance of the Progress Sax-Attributes object and retrieves the handle
        * variable associated with it.
        *
        * @param    h
        *           The handle where the new resource will be set as the referent.
        */
       public static void createSaxAttributes(handle h)
       {
          createSaxAttributes(h, (character) null);
       }

       /**
        * Constructs a new instance of the Progress Sax-Attributes object and retrieves the handle
        * variable associated with it.
        *
        * @param    h
        *           The handle where the new resource will be set as the referent.
        * @param    widgetPool
        *           The name of the widget pool. <code>null</code> if the unnamed pool must be used.
        */
       public static void createSaxAttributes(handle h, String widgetPool)
       {
          createSaxAttributes(h, (widgetPool == null ? null : new character(widgetPool)));
       }

       /**
        * Constructs a new instance of the Progress Sax-Attributes object and retrieves the handle
        * variable associated with it.
        *
        * @param    h
        *           The handle where the new resource will be set as the referent.
        * @param    widgetPool
        *           The name of the widget pool. <code>null</code> if the unnamed pool must be used.
        */
       public static void createSaxAttributes(handle h, character widgetPool)
       {
          // validate widget pool before anything else!!!
          if (!WidgetPool.validWidgetPool(widgetPool))
          {
             return;
          }

          SaxAttributesImpl res = new SaxAttributesImpl();
          h.assign(res);

          res.addToPool(widgetPool);
       }

       /**
        * Constructs a new instance of the Progress Sax-Reader object and retrieves the handle
        * variable associated with it.
        *
        * @param    h
        *           The handle where the new resource will be set as the referent.
        */
       public static void createSaxReader(handle h)
       {
          createSaxReader(h, (character) null);
       }

       /**
        * Constructs a new instance of the Progress Sax-Reader object and retrieves the handle
        * variable associated with it.
        *
        * @param    h
        *           The handle where the new resource will be set as the referent.
        * @param    widgetPool
        *           The name of the widget pool. <code>null</code> if the unnamed pool must be used.
        */
       public static void createSaxReader(handle h, String widgetPool)
       {
          createSaxReader(h, (widgetPool == null ? null : new character(widgetPool)));
       }

       /**
        * Constructs a new instance of the Progress Sax-Reader object and retrieves the handle
        * variable associated with it.
        *
        * @param    h
        *           The handle where the new resource will be set as the referent.
        * @param    widgetPool
        *           The name of the widget pool. <code>null</code> if the unnamed pool must be used.
        */
       public static void createSaxReader(handle h, character widgetPool)
       {
          // validate widget pool before anything else!!!
          if (!WidgetPool.validWidgetPool(widgetPool))
          {
             return;
          }

          SaxReaderImpl res = new SaxReaderImpl();
          h.assign(res);

          res.addToPool(widgetPool);
       }

       /**
        * Constructs a new instance of the Progress Sax-Writer object and retrieves the handle
        * variable associated with it.
        *
        * @param    h
        *           The handle where the new resource will be set as the referent.
        */
       public static void createSaxWriter(handle h)
       {
          createSaxWriter(h, (character) null);
       }

       /**
        * Constructs a new instance of the Progress Sax-Writer object and retrieves the handle
        * variable associated with it.
        *
        * @param    h
        *           The handle where the new resource will be set as the referent.
        * @param    widgetPool
        *           The name of the widget pool. <code>null</code> if the unnamed pool must be used.
        */
       public static void createSaxWriter(handle h, String widgetPool)
       {
          createSaxWriter(h, (widgetPool == null ? null : new character(widgetPool)));
       }

       /**
        * Constructs a new instance of the Progress Sax-Writer object and retrieves the handle
        * variable associated with it.
        *
        * @param    h
        *           The handle where the new resource will be set as the referent.
        * @param    widgetPool
        *           The name of the widget pool. <code>null</code> if the unnamed pool must be used.
        */
       public static void createSaxWriter(handle h, character widgetPool)
       {
          // validate widget pool before anything else!!!
          if (!WidgetPool.validWidgetPool(widgetPool))
          {
             return;
          }

          SaxWriterImpl res = new SaxWriterImpl();
          h.assign(res);

          res.addToPool(widgetPool);
       }

       /**
        * Resolve the given numeric token type to a character ABL token type.
        *
        * @param   nodeId
        *          integer constant representing node id.
        *
        * @return  character token type or <code>unknow</code> if node id could not be resolved.
        */
       public static character convertNodeToAblNode(int nodeId)
       {
          return new character(nodeIdToNodeName.get(nodeId));
       }

       /**
        * Resolve the given numeric token type to a token name. The lookup is case-insensitive.
        *
        * @param   nodeName
        *          String constant representing token name.
        *
        * @return  int token type or <code>null</code> if name could not
        *          be resolved.
        */
       public static Integer convertAblNodeToNodeId(String nodeName)
       {
          if (nodeName == null)
          {
             return null;
          }
          return nodeNameToNodeId.get(nodeName.toUpperCase());
       }

       /**
        * Constructs a new instance of the Progress X-Node-Reference object and
        * retrieves the handle variable associated with it.
        *
        * @param    h
        *           The handle where the new resource will be set as the referent.
        *           The handle should contains resource with type of XNodeRef.
        * @param    docHandle
        *           handle should be valid and should contains the valid X-Document object to
        *           create X-Node under.
        * @param    name
        *           The name of the node to create.
        * @param    type
        *           The type of the node to create.
        *
        * @throws   DOMException
        *           if type is cannot resolved. Or if name is invalid for XML.
        */
       static void createXNodeReference(handle h,
                                        handle docHandle,
                                        character name,
                                        character type)
       {
          if (docHandle == null || !docHandle._isValid())
          {
             throw new IllegalArgumentException("docHandle must be not null and valid");
          }
          if (!XEntityImpl.isXNodeHandle(h))
          {
             throw new IllegalArgumentException("handle should contains resource with type of " +
                                                "XNodeRef");
          }

          Document doc = getDocument(docHandle);
          if (doc == null)
          {
             throw new IllegalArgumentException("docHandle must be of type DOCUMENT_NODE");
          }

          final Integer nodeId;
          if (type == null || type.isUnknown())
          {
             nodeId = ELEMENT_NODE;
          }
          else
          {
             nodeId = convertAblNodeToNodeId(type.toStringMessage());
          }

          if (nodeId == null)
          {
             throw new DOMException(DOMException.SYNTAX_ERR, "Unknown type");
          }

          String nameStr = null;
          if (!name.isUnknown())
          {
             nameStr = name.toStringMessage();
          }

          final Node node;

          switch (nodeId)
          {
             case ATTRIBUTE_NODE:
                node = doc.createAttribute(nameStr);
                break;
             case CDATA_SECTION_NODE:
                node = doc.createCDATASection("");
                break;
             case COMMENT_NODE:
                node = doc.createComment("");
                break;
             case DOCUMENT_FRAGMENT_NODE:
                node = doc.createDocumentFragment();
                break;
             case ELEMENT_NODE:
                node = doc.createElement(nameStr);
                break;
             case ENTITY_REFERENCE_NODE:
                node = doc.createEntityReference(nameStr);
                break;
             case PROCESSING_INSTRUCTION_NODE:
                node = doc.createProcessingInstruction(nameStr, "");
                break;
             case TEXT_NODE:
                node = doc.createTextNode("");
                break;
             default:
                throw new DOMException(DOMException.TYPE_MISMATCH_ERR, "Unknown type");
          }

          ((XEntityImpl)h.getResource()).setNode(node);
       }

       /**
        * Extract {@link Document} from valid handle, which contains {@link XDocumentImpl} class.
        * If handle is not valid or doesn't inherit {@link XDocumentImpl} than return null.
        *
        * @param    docHandle
        *           The handle reference.
        *
        * @return   {@link Document} or <code>null</code> if handle invalid or doesn't contains
        *           Document object.
        */
       private static Document getDocument(handle docHandle) {
          if (!docHandle._isValid())
          {
             return null;
          }
          Object resource = docHandle.get();

          XDocumentImpl xDocument;

          if (resource instanceof XDocumentImpl)
          {
             xDocument = (XDocumentImpl) resource;
          }
          else
          {
             return null;
          }

          return (Document) xDocument.getNode();
       }
    }