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