com.adobe.xfa.form.FormModel Maven / Gradle / Ivy
Show all versions of aem-sdk-api Show documentation
/*
* ADOBE CONFIDENTIAL
*
* Copyright 2005 Adobe Systems Incorporated All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains the property of
* Adobe Systems Incorporated and its suppliers, if any. The intellectual and
* technical concepts contained herein are proprietary to Adobe Systems
* Incorporated and its suppliers and may be covered by U.S. and Foreign
* Patents, patents in process, and are protected by trade secret or copyright
* law. Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained from
* Adobe Systems Incorporated.
*/
// Adobe Patent or Adobe Patent Pending Invention Included Within this File
package com.adobe.xfa.form;
import com.adobe.xfa.AppModel;
import com.adobe.xfa.ArrayNodeList;
import com.adobe.xfa.Attribute;
import com.adobe.xfa.ChildReln;
import com.adobe.xfa.EventPseudoModel.EventInfo;
import com.adobe.xfa.content.Content;
import com.adobe.xfa.content.ExDataValue;
import com.adobe.xfa.content.TextValue;
import com.adobe.xfa.Chars;
import com.adobe.xfa.Comment;
import com.adobe.xfa.Delta;
import com.adobe.xfa.Dispatcher;
import com.adobe.xfa.Document;
import com.adobe.xfa.DOMSaveOptions;
import com.adobe.xfa.Element;
import com.adobe.xfa.EnumAttr;
import com.adobe.xfa.EnumValue;
import com.adobe.xfa.EventManager;
import com.adobe.xfa.EventPseudoModel;
import com.adobe.xfa.Generator;
import com.adobe.xfa.HostPseudoModel;
import com.adobe.xfa.Int;
import com.adobe.xfa.LogMessage;
import com.adobe.xfa.LogMessenger;
import com.adobe.xfa.Model;
import com.adobe.xfa.ModelPeer;
import com.adobe.xfa.Node;
import com.adobe.xfa.NodeList;
import com.adobe.xfa.Obj;
import com.adobe.xfa.Packet;
import com.adobe.xfa.ProcessingInstruction;
import com.adobe.xfa.ProtoableNode;
import com.adobe.xfa.RichTextNode;
import com.adobe.xfa.Schema;
import com.adobe.xfa.ScriptHandler;
import com.adobe.xfa.ScriptTable;
import com.adobe.xfa.service.storage.XMLStorage;
import com.adobe.xfa.SOMParser;
import com.adobe.xfa.StringAttr;
import com.adobe.xfa.STRS;
import com.adobe.xfa.TextNode;
import com.adobe.xfa.XFA;
import com.adobe.xfa.XFAList;
import com.adobe.xfa.XMLMultiSelectNode;
import com.adobe.xfa.data.DataModel;
import com.adobe.xfa.data.DataNode;
import com.adobe.xfa.data.DataWindow;
import com.adobe.xfa.template.Items;
import com.adobe.xfa.template.TemplateModel;
import com.adobe.xfa.template.Value;
import com.adobe.xfa.template.containers.Container;
import com.adobe.xfa.template.containers.Draw;
import com.adobe.xfa.template.containers.ExclGroup;
import com.adobe.xfa.template.containers.Field;
import com.adobe.xfa.template.containers.PageArea;
import com.adobe.xfa.template.containers.PageSet;
import com.adobe.xfa.template.containers.Subform;
import com.adobe.xfa.template.containers.SubformSet;
import com.adobe.xfa.ut.Assertions;
import com.adobe.xfa.ut.Base64;
import com.adobe.xfa.ut.BooleanHolder;
import com.adobe.xfa.ut.ExFull;
import com.adobe.xfa.ut.FindBugsSuppress;
import com.adobe.xfa.ut.IntegerHolder;
import com.adobe.xfa.ut.LcData;
import com.adobe.xfa.ut.MsgFormat;
import com.adobe.xfa.ut.MsgFormatPos;
import com.adobe.xfa.ut.ObjectHolder;
import com.adobe.xfa.ut.Peer;
import com.adobe.xfa.ut.PictureFmt;
import com.adobe.xfa.ut.ResId;
import com.adobe.xfa.ut.StringHolder;
import com.adobe.xfa.ut.StringUtils;
import com.adobe.xfa.ut.trace.TraceHandler.TimingType;
import com.adobe.xfa.ut.trace.Trace;
import com.adobe.xfa.ut.trace.TraceTimer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.io.InputStream;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
/**
* A class to represent the result of joining a template and data.
*/
public class FormModel extends Model {
/**
* @exclude from published api.
*/
public interface ConnectHandler {
public boolean handleConnect(
Node node,
String sConnectionRootRef,
String sConnectRef,
String sConnectPicture,
Object handlerData,
ObjectHolder ioRecursingData);
}
private static final ConnectHandler mConnectExportHandler = new ConnectHandler() {
// Adobe patent application tracking # P624, entitled Form-based Data Storage And Retrieval, inventors: Matveief,Young,Solc
public boolean handleConnect(Node formNode, String sConnectionRootRef, String sConnectRef, String sConnectPicture, Object handlerData, ObjectHolder ioRecursingData) {
Model formModel = formNode.getModel();
assert(formModel != null);
AppModel appModel = (AppModel)formModel.getXFAParent();
DataNode dataNode = null;
DataNode connectionDataNode = (DataNode)appModel.resolveNode(sConnectionRootRef, false, false, true);
assert(connectionDataNode != null);
DataNode parentNode = ioRecursingData.value;
NodeList nodes;
if (parentNode != null) {
nodes = parentNode.resolveNodes(sConnectRef, true, false, true);
}
else {
nodes = connectionDataNode.resolveNodes(sConnectRef, true, false, true);
}
boolean bMultiple = isSomMultiple(sConnectRef);
int nLen = nodes.length();
for (int i = 0; i < nLen; i++) {
Node resolvedNode = (Node)nodes.item(i);
// did we already use the node?
if (bMultiple && resolvedNode.isMapped()) {
continue;
}
else if (dataNode == null) {
dataNode = (DataNode)resolvedNode; // did we use it?
break;
}
}
// do we need to create one ?
if (dataNode == null) {
DataModel dataModel = DataModel.getDataModel(appModel, false, false);
boolean bUseDV = useDV(formNode);
if (parentNode != null) {
// use the parent node as the context
dataNode = (DataNode)dataModel.resolveRef(sConnectRef, parentNode, bUseDV, false);
}
else
dataNode = (DataNode)dataModel.resolveRef(sConnectRef, connectionDataNode, bUseDV, false);
}
if (dataNode != null) {
if (dataNode.getIsDDPlaceholder()) {
// clear place holder flag when the node is used
dataNode.setIsDDPlaceholder(false);
}
if (formNode instanceof FormField) {
((FormField)formNode).setDataNode(dataNode, true, false, sConnectPicture, true);
((FormModel)formModel).consumeDataNode(null, dataNode, FormModel.DatasetSelector.MAIN_DATASET);
}
else if (formNode instanceof FormExclGroup) {
((FormExclGroup)formNode).setDataNode(dataNode, true, false);
((FormModel)formModel).consumeDataNode(null, dataNode, FormModel.DatasetSelector.MAIN_DATASET);
}
else if (formNode instanceof FormSubform) {
// watson bug 1525961 marked the node as mapped so we don't use it again.
((FormModel)formModel).consumeDataNode(null, dataNode, FormModel.DatasetSelector.MAIN_DATASET);
ioRecursingData.value = dataNode; // set the data node as the new parent
}
}
return true; // always continue recursing
}
};
/**
* Support for connectionSet and connectionData
*/
private static final ConnectHandler mConnectImportHandler = new ConnectHandler() {
// Adobe patent application tracking # P624, entitled Form-based Data Storage And Retrieval, inventors: Matveief,Young,Solc
public boolean handleConnect(Node formNode, String sConnectionRootRef, String sConnectRef, String sConnectPicture, Object handlerData, ObjectHolder ioRecursingData) {
FormModel formModel = (FormModel)formNode.getModel();
assert(formModel != null);
AppModel appModel = formNode.getModel().getAppModel();
DataNode parentNode = ioRecursingData.value;
// refs can be absolute or relative
String sDataRef = sConnectRef;
boolean bMultiple = isSomMultiple(sDataRef);
NodeList resolvedDataNodes;
if (parentNode != null)
resolvedDataNodes = parentNode.resolveNodes(sDataRef, true, false, true);
else
resolvedDataNodes = appModel.resolveNodes(sDataRef, true, false, true);
if (resolvedDataNodes.length() != 0) {
DataNode dataNode = null;
for (int i = 0; i < resolvedDataNodes.length(); i++) {
DataNode item = (DataNode)resolvedDataNodes.item(i);
// if there are more than one in the list - happy dynamic data!!
if (bMultiple && item.isMapped())
continue;
dataNode = item;
break;
}
if (dataNode != null) {
if (formNode instanceof FormField) {
((FormField)formNode).setDataNode(dataNode, false, false, sConnectPicture, false/*CL#708930*/);
formModel.consumeDataNode(null, dataNode, FormModel.DatasetSelector.MAIN_DATASET);
}
else if (formNode instanceof FormExclGroup) {
((FormExclGroup)formNode).setDataNode(dataNode, false, false);
formModel.consumeDataNode(null, dataNode, FormModel.DatasetSelector.MAIN_DATASET);
}
else if (formNode instanceof FormSubform) {
// sets this to be the new parent if we are at a subform - equivalent to the poConnectionDataParent in createAndMatchNode
formModel.consumeDataNode(null, dataNode, FormModel.DatasetSelector.MAIN_DATASET);
ioRecursingData.value = dataNode;
}
if (formNode instanceof Container) {
((FormModel)formNode.getModel()).setConnectionDataContextInfo((Container)formNode, dataNode);
}
}
}
return true; // always continue recursing
}
};
private static final ConnectHandler mConnectImportPermCheckHandler = new ConnectHandler() {
/* Watson bug 1368015 do a first pass to see if this will violate XFA perms.*/
public boolean handleConnect(Node formNode, String sConnectionRootRef, String sConnectRef, String sConnectPicture, Object handlerData, ObjectHolder ioRecursingData) {
assert handlerData != null;
BooleanHolder overallResult = (BooleanHolder)handlerData;
AppModel appModel = formNode.getModel().getAppModel();
Node parentNode = ioRecursingData.value;
// refs can be absolute or relative
String sDataRef = sConnectRef;
boolean bMultiple = isSomMultiple(sDataRef);
NodeList resolvedDataNodes;
if (parentNode != null)
resolvedDataNodes = parentNode.resolveNodes(sDataRef, true, false, true);
else
resolvedDataNodes = appModel.resolveNodes(sDataRef, true, false, true);
if (resolvedDataNodes.length() != 0) {
DataNode dataNode = null;
for (int i = 0; i < resolvedDataNodes.length(); i++) {
DataNode item = (DataNode)resolvedDataNodes.item(i);
// if there are more than one in the list - happy dynamic data!!
if (bMultiple && item.isMapped())
continue;
dataNode = item;
break;
}
if (dataNode != null) {
// update the parent for the next call
if (formNode instanceof FormSubform)
ioRecursingData.value = dataNode;
// check perms
if (!formNode.checkPerms() || !formNode.checkAncestorPerms())
overallResult.value = false;
}
}
return overallResult.value;
}
};
/**
* @exclude from published api.
*/
public abstract static class Execute {
public abstract Object clone();
public abstract void execute(String sConnection,
int meRunAt,
int meExecuteType);
}
/**
* Provides a mechanism for FormModel to execute an event on a server.
*
* @exclude from published api.
*/
public abstract static class ServerExchange {
/**
* Allows this implementation to react to the changed data after the
* FormModel has loaded the data from the response. This is called as
* the last step after data has been successfully exchanged with the
* server.
*
* The default implementation does nothing.
*/
public void remerge() {}
/**
* Sends a request to the server and returns the server's response. The
* derived class must implement the transfer mechanism. The contents in
* each direction are formatted as UTF-8 encoded XDP data. The request
* includes the DataModel contents, as well as an execEvent packet (with
* context and activity attributes) that describe the event to be
* executed on the server.
*
* @param request
* a buffer containing the data to be sent in the server
* request.
* @return a buffer containing the server response, this is expected to
* be UTF-8 encoded XDP data.
*/
public abstract byte[] sendToServer(byte[] request);
}
/**
* @exclude from published api.
*/
public abstract static class Submit {
/**
* @exclude from published api.
*/
public static class SubmitParams {
@FindBugsSuppress(pattern="EI_EXPOSE_REP,EI_EXPOSE_REP2")
private final String[] mPackets;
private final String msSubmitUrl;
private final int meFormat;
private final boolean mbEmbedPDF;
private String msTextEncoding;
// secure delivery
private String msCertificate;
private List mSignDispatchers;
public SubmitParams(String[] packets,
String sSubmitUrl,
int eFormat,
String sTextEncoding,
boolean bEmbedPDF) {
mPackets = packets;
msSubmitUrl = sSubmitUrl;
meFormat = eFormat;
msTextEncoding = sTextEncoding;
mbEmbedPDF = bEmbedPDF;
}
public String getCertificate() { return msCertificate; }
public boolean getEmbedPDF() { return mbEmbedPDF; }
public int getFormat() { return meFormat; }
public String[] getPackets() { return mPackets; }
public List getSignDispatchers() { return mSignDispatchers; }
public String getSubmitUrl() { return msSubmitUrl; }
public String getTextEncoding() { return msTextEncoding; }
public void setCertificate(String sCertificate) { msCertificate = sCertificate; }
public void setSignDispatchers(List poSD) { mSignDispatchers = poSD; }
public void setTextEncoding(String sTextEncoding) { msTextEncoding = sTextEncoding; }
}
public abstract Object clone();
public abstract void submit(SubmitParams params);
//bug 2426847v
public abstract void setPacketToIgnore(Node pPacket);
}
/**
* A base class that an implementation can derive from to
* interact with the form validation process. Before performing each
* validation test, the validation framework calls back to a Validate
* instance to determine whether that kind of validation test is enabled.
* Each time a validation failure occurs, the validation framework calls
* back to a Validate instance to record the failure.
*
* This default implementation has all kinds of validations enabled by
* default. It takes no action on validation failures, except to increment
* the number of failures.
*/
public static class Validate {
private boolean mbScriptTestEnabled; // Should script validation be performed?
private boolean mbNullTestEnabled; // Should nullTest validation be performed?
private boolean mbFormatTestEnabled; // Should formatTest validation be performed?
private boolean mbBarcodeTestEnabled; // Should barcodeTest validation be performed?
/**
* @exclude from published api.
*/
protected int mnNumFailures;
/**
* Initializes a newly created Validate
object so that
* all kinds validation tests are enabled.
*/
public Validate() {
mbScriptTestEnabled = true;
mbNullTestEnabled = true;
mbFormatTestEnabled = true;
mbBarcodeTestEnabled = true;
}
/**
* Initializes a newly created Validate
object so that
* specified validation tests are enabled.
* @param bScriptTestEnabled determines whether script validations are enabled
* @param bNullTestEnabled determines whether null validations are enabled
* @param bFormatTestEnabled determines whether format validations are enabled
* @param bBarcodeTestEnabled determines whether barcode validations are enabled
*/
public Validate(boolean bScriptTestEnabled, boolean bNullTestEnabled, boolean bFormatTestEnabled, boolean bBarcodeTestEnabled) {
mbScriptTestEnabled = bScriptTestEnabled;
mbNullTestEnabled = bNullTestEnabled;
mbFormatTestEnabled = bFormatTestEnabled;
mbBarcodeTestEnabled = bBarcodeTestEnabled;
}
/**
* Is called when a validation pass is started.
*/
public void onValidateStart() {
}
/**
* Is called when a validation pass ends.
*/
public void onValidateEnd() {
}
/**
* Creates a copy of this Validate
.
* The set of tests enabled is copied, and the number of failures is set to zero.
*/
public Validate clone() {
return new Validate(mbScriptTestEnabled, mbNullTestEnabled, mbFormatTestEnabled, mbBarcodeTestEnabled);
}
/**
* Determines if barcode validation tests are enabled.
*
* @return true
if barcode validation tests are enabled;
* otherwise false
*/
public boolean isBarcodeTestEnabled() { return mbBarcodeTestEnabled; }
/**
* Sets whether barcode validation tests are to be enabled.
*
* @param bEnabled
* true
if barcode tests are to be performed.
*/
public void setBarcodeTestEnabled(boolean bEnabled) { mbBarcodeTestEnabled = bEnabled; }
/**
* Determines if format validation tests are enabled.
*
* @return true
if format validation tests are enabled;
* otherwise false
*/
public boolean isFormatTestEnabled() { return mbFormatTestEnabled; }
/**
* Sets whether format validation tests are to be enabled.
*
* @param bEnabled
* true
if format tests are to be performed.
*/
public void setFormatTestEnabled(boolean bEnabled) { mbFormatTestEnabled = bEnabled; }
/**
* Determines if null validation tests are enabled.
*
* @return true
if null validation tests are enabled;
* otherwise false
*/
public boolean isNullTestEnabled() { return mbNullTestEnabled; }
/**
* Sets whether null validation tests are to be enabled.
*
* @param bEnabled
* true
if null tests are to be performed.
*/
public void setNullTestEnabled(boolean bEnabled) { mbNullTestEnabled = bEnabled; }
/**
* Determines if script validation tests are enabled.
*
* @return true
if script validation tests are enabled;
* otherwise false
*/
public boolean isScriptTestEnabled() { return mbScriptTestEnabled; }
/**
* Sets whether script validation tests are to be enabled.
*
* @param bEnabled
* true
if script tests are to be performed.
*/
public void setScriptTestEnabled(boolean bEnabled) { mbScriptTestEnabled = bEnabled; }
/**
* Is called by the validation framework when a barcode validation test
* fails. This base implementation increments the number of failures and
* returns true
.
*
* @param field
* the field containing the barcode test
* @param sValidationMessage
* the validation error message
* @return true
if validation should continue
*/
public boolean onValidateBarcodeTestFailed(
FormField field,
String sValidationMessage) {
mnNumFailures++;
return true;
}
/**
* Is called by the validation framework when a format validation test
* fails. This base implementation increments the number of failures and
* returns true
.
*
* @param field
* the field containing the format test
* @param sValidationMessage
* the validation error message
* @param bDisableValidate
* indicates whether future validations for this field should be disabled
* @return true
if validation should continue
*/
public boolean onValidateFormatTestFailed(
FormField field,
String sValidationMessage,
BooleanHolder bDisableValidate) {
mnNumFailures++;
return true;
}
/**
* Is called by the validation framework when a null validation test fails.
* This base implementation increments the number of failures and
* returns true
.
*
* @param node
* the node containing the null test
* @param sValidationMessage
* the validation error message
* @param bDisableValidate
* indicates whether future validations for this ProtoableNode should be disabled
* @return true
if validation should continue
*/
public boolean onValidateNullTestFailed(
ProtoableNode node,
String sValidationMessage,
BooleanHolder bDisableValidate) {
mnNumFailures++;
return true;
}
/**
* Is called by the validation framework when a script validation test
* fails. This base implementation increments the number of failures and
* returns true
.
*
* @param node
* the node containing the script test
* @param sScript
* the text of the validation script that failed
* @param sLanguage
* the language of the script
* @param sValidationMessage
* the validation error message
* @param bDisableValidate
* indicates whether future validations for this ProtoableNode should be disabled
* @return true
if validation should continue
*/
public boolean onValidateScriptFailed(
ProtoableNode node,
String sScript,
String sLanguage,
String sValidationMessage,
BooleanHolder bDisableValidate) {
mnNumFailures++;
return true;
}
/**
* Returns the number of validation failures since the fail count was
* last reset.
*
* @return the number of validation failures
*/
public int getFailCount() { return mnNumFailures; }
/**
* Sets the number of validation failures to zero. The validation
* framework calls this method before it starts each validation pass
* initiated by:
* a call to an execValidate method
* an execute or submit action of an event, or
* an excit event of a FormSubform or FormExclGroup.
*
* This method is not called when
* {@link FormModel#recalculate(boolean, FormModel.Validate, boolean)}
* is called.
*/
public void resetFailCount() { mnNumFailures = 0; }
/**
* Validates a barcode. The default implementation always returns
* true
.
*
* @param element
* the barcode form Element.
* @param sBarcodeType
* a String that identifies the barcode pattern
* @param sValue
* the barcode value
* @return true
if the barcode is valid
*/
public boolean validateBarcode(
Element element,
String sBarcodeType,
String sValue) {
return true;
}
}
private static class ExecuteInfo {
public final String msEventContext;
//public final String msConnection;
//public final int meRunAt; // = EnumAttr.RUNAT_BOTH;
//public final int meExecuteType; // = EnumAttr.EXECUTETYPE_IMPORT;
public final ProtoableNode mExecuteContextNode;
public ExecuteInfo(
String sEventContext,
//String sConnection, int eRunAt, int eExecuteType,
ProtoableNode executeContextNode) {
msEventContext = sEventContext;
//msConnection = sConnection;
//meRunAt = eRunAt;
//meExecuteType = eExecuteType;
mExecuteContextNode = executeContextNode;
}
}
private static class ScriptInfo {
public final String msScript;
public final String msScriptLanguage;
public final String msEventContext;
public final int meRunAt; // = EnumAttr.RUNAT_BOTH;
public final ProtoableNode mScriptContextNode;
public String msTarget = null;
public ScriptInfo(String sScript, String sScriptLanguage, String sEventContext, int eRunAt, ProtoableNode scriptContextNode) {
msScript = sScript;
msScriptLanguage = sScriptLanguage;
msEventContext = sEventContext;
meRunAt = eRunAt;
mScriptContextNode = scriptContextNode;
}
}
private static class SubmitInfo {
// public final String msTarget;
// public final String msFormat;
// public final String msTextEncoding;
// public final String msXDPContent;
// public final boolean mbEmbedPDF;
public final String msEventContext;
public final ProtoableNode mSubmitContextNode;
public SubmitInfo(
//String sTarget, String sFormat, String sTextEncoding, String sXDPContent, boolean bEmbedPDF,
String sEventContext, ProtoableNode submitContextNode) {
// msTarget = sTarget;
// msFormat = sFormat;
// msTextEncoding = sTextEncoding;
// msXDPContent = sXDPContent;
// mbEmbedPDF = bEmbedPDF;
msEventContext = sEventContext;
mSubmitContextNode = submitContextNode;
}
}
private static class ValidateInfo {
public final String msScript;
public final String msScriptLanguage;
// public final boolean mbNullTest;
// public final boolean mbFormatTest;
// public final boolean mbScriptTest;
// public final boolean mbBarcodeTest;
public final String msEventContext;
public final String msBarcodeType;
public final int meRunAt; // = EnumAttr.RUNAT_BOTH;
public final ProtoableNode mScriptContextNode;
public ValidateInfo(
String sScript, String sScriptLanguage,
//boolean bNullTest, boolean bFormatTest, boolean bScriptTest, boolean bBarcodeTest,
String sEventContext, String sBarcodeType, int eRunAt, ProtoableNode scriptContextNode) {
msScript = sScript;
msScriptLanguage = sScriptLanguage;
// mbNullTest = bNullTest;
// mbFormatTest = bFormatTest;
// mbScriptTest = bScriptTest;
// mbBarcodeTest = bBarcodeTest;
msEventContext = sEventContext;
msBarcodeType = sBarcodeType;
meRunAt = eRunAt;
mScriptContextNode = scriptContextNode;
}
}
private static class LayoutContentInfo {
public final Element mNode;
public boolean mbInitializeOccurred;
// Convenience constructor
public LayoutContentInfo(Element node) {
mNode = node;
//mbInitializeOccurred = false;
}
}
private final static FormSchema gsFormSchema = new FormSchema();
// masks for events
/** @exclude from published api */
final static int XFAEVENTTYPE_EVENTS = 1;
/** @exclude from published api */
final static int XFAEVENTTYPE_CALCULATE = 2;
/** @exclude from published api */
final static int XFAEVENTTYPE_VALIDATE = 4;
/** @exclude from published api */
final static int XFAEVENTTYPE_ALL = 7;
/**
* if a script executes this many times in one recalculation, then a cyclic
* dependency is assumed to exist.
*/
private static final int CYCLE_MAX = 10;
private static final Boolean UPDATE_DATA = true;
/**
* @exclude from published api.
*/
public enum DatasetSelector {
MAIN_DATASET,
ALT_DATASET
}
// Adobe patent application tracking # P624, entitled Form-based Data Storage And Retrieval, inventors: Matveief,Young,Solc
private static boolean getConnectSOMStrings(Node node,
String strConnectionName,
int eUsage,
StringHolder outConnectionRootRef,
StringHolder outConnectRef,
StringHolder sConnectPicture /* = null */) {
Element connectNode = null;
if (node instanceof Field || node instanceof ExclGroup || node instanceof Subform ) {
connectNode = ((Container)node).getConnectNode(strConnectionName, eUsage, false);
}
if (connectNode != null) {
// get the ref
outConnectRef.value = connectNode.getAttribute(XFA.REFTAG).toString();
// this ref will be in the form "Body.etc..."
// the actual data will reside in
// "xdp.datasets.connectionData.connectionName.body.etc..."
outConnectionRootRef.value = "!" + XFA.CONNECTIONDATA + "." + strConnectionName;
if (sConnectPicture != null) {
Element picture = connectNode.getElement(XFA.PICTURETAG, true, 0, false, false);
if (picture != null) {
TextNode textNode = picture.getText(true, false, false);
if (textNode != null) {
sConnectPicture.value = textNode.getValue();
}
}
}
return true;
}
return false;
}
/**
* Gets the DataNode from the form node.
*
* @exclude from published api.
*/
static DataNode getDataNode(Node formNode) {
if (null != formNode) {
if (formNode instanceof FormField) {
return ((FormField)formNode).getDataNode();
}
else if (formNode instanceof FormChoiceListField) {
return ((FormChoiceListField)formNode).getDataNode();
}
else if (formNode instanceof FormSubform) {
return ((FormSubform)formNode).getDataNode();
}
else if (formNode instanceof FormExclGroup) {
return ((FormExclGroup)formNode).getDataNode();
}
}
return null;
}
/**
* Returns the FormModel held within the AppModel.
*
* @param appModel
* the AppModel to search
* @param bCreateIfNotFound
* if true
, and the FormModel does not exist,
* then one will be created and returned; if false
* and the FormModel does not exist, null
is
* returned
*
* @return the FormModel contained within appModel; null
if
* not found and bCreateIfNotFound is false
.
*/
public static FormModel getFormModel(AppModel appModel, boolean bCreateIfNotFound /* = true */) {
FormModel form = null;
if (appModel != null) {
TemplateModel template = null;
for (Node child = appModel.getFirstXFAChild(); child != null; child = child.getNextXFASibling()) {
if (child instanceof FormModel) {
form = (FormModel)child;
}
else if (child instanceof TemplateModel) {
template = (TemplateModel)child;
}
}
if (bCreateIfNotFound && form == null && template != null) {
FormModel newFormModel = new FormModel(appModel, null);
// FormModel is always in its own document
newFormModel.setDocument(Document.createDocument(appModel));
newFormModel.setXmlPeer(
new ModelPeer(
newFormModel.getDocument(), null,
STRS.XFAFORMNS_CURRENT, XFA.FORM, XFA.FORM,
null, newFormModel));
appModel.notifyPeers(Peer.CHILD_ADDED, XFA.FORM, newFormModel);
return getFormModel(appModel, false);
}
}
// if app model is empty, form model will be too!
return form;
}
/**
* Gets the first ancestor that is mapped
*
* @param formNode
* the start node
* @return the first mapped parent, null if none found.
*
* @exclude from published api.
*/
static Element getMappedParent(Node formNode) {
// Search up the Form DOM hierarchy for a mapped node
if (formNode == null) {
return null;
}
Element parent = formNode.getXFAParent();
if (parent == null) {
return parent;
}
else if (parent.isMapped()) {
DataNode dataNode = getDataNode(parent);
if (dataNode == null)
return getMappedParent(parent);
else
return parent;
}
else {
return getMappedParent(parent);
}
}
private static Schema getModelSchema() {
return gsFormSchema;
}
/**
* Gets the correct text for the given validate method.
*
* @exclude from published api.
*/
static String getValidationMessage(Element validateNode, String aType) {
return TemplateModel.getValidationMessage(validateNode, aType);
}
/**
* Recursively checks a data description to ensure that it is suitable for
* incremental merge.
*
* @param dataDescriptionNode
* the data description node to search
* @return true if incremental merge should be attempted; false if the data
* description supports variable occurrences of subforms, or a
* "choice" or "unordered" relation is supported.
*/
private static boolean incrementalMergeCheckDataDescription(Node dataDescriptionNode) {
if (dataDescriptionNode instanceof Element) {
Element element = (Element)dataDescriptionNode;
int nMinOccur = 1;
int nMaxOccur = 1;
int index;
index = element.findAttr(STRS.DATADESCRIPTIONURI, "minOccur");
if (index != -1) {
String sMinOccur = element.getAttrVal(index);
if (!StringUtils.isEmpty(sMinOccur)) {
try { nMinOccur = Integer.parseInt(sMinOccur); }
catch (NumberFormatException ex) {}
}
}
index = element.findAttr(STRS.DATADESCRIPTIONURI, "maxOccur");
if (index != -1) {
String sMaxOccur = element.getAttrVal(index);
if (!StringUtils.isEmpty(sMaxOccur)) {
try { nMaxOccur = Integer.parseInt(sMaxOccur); }
catch (NumberFormatException ex) {}
}
}
// If variable occurrences are allowed, we can't do an
// incremental merge.
boolean bVariableOccurrence = (nMinOccur != nMaxOccur || nMinOccur == -1 || nMaxOccur == -1);
if (bVariableOccurrence)
return false;
// Can't support relation=choice or relation=unordered
index = element.findAttr(STRS.DATADESCRIPTIONURI, "model");
if (index != -1) {
String sModel = element.getAttrVal(index);
if (sModel.equals("choice") || sModel.equals("unordered"))
return false;
}
// Recursively apply search to child elements
for (Node child = element.getFirstXFAChild(); child != null; child = child.getNextXFASibling()) {
if ( ! incrementalMergeCheckDataDescription(child))
return false;
}
}
return true;
}
// Adobe patent application tracking # P624, entitled "Form-based Data Storage And Retrieval", inventors: Matveief,Young,Solc
/**
* @exclude from published api.
*/
public static void recurseConnectOnNode(Node node, String strConnectionName,
int eUsage, ConnectHandler handler, Object handlerData) {
ObjectHolder recursingData = new ObjectHolder();
recurseConnectOnNodeHelper(node, strConnectionName, eUsage, handler, handlerData, recursingData);
}
// Adobe patent application tracking # P624, entitled Form-based Data Storage And Retrieval, inventors: Matveief,Young,Solc
private static boolean recurseConnectOnNodeHelper(Node node, String strConnectionName,
int eUsage, ConnectHandler handler, Object handlerData, ObjectHolder recursingData) {
// CAUTION: input node to this method is not necessarily a form node
// - can be template node when called from wsdl connection set proxy
StringHolder strConnectionRootRef = new StringHolder();
StringHolder strConnectRef = new StringHolder();
StringHolder strConnectPictureRef = new StringHolder();
ObjectHolder tempioRecursingData = new ObjectHolder();
boolean bContinue = true;
for (Node child = node.getFirstXFAChild(); child != null; child = child.getNextXFASibling()) {
strConnectionRootRef.value = null;
strConnectRef.value = null;
strConnectPictureRef.value = "";
tempioRecursingData.value = recursingData.value;
if (child instanceof Container) {
if (getConnectSOMStrings(child, strConnectionName, eUsage, strConnectionRootRef, strConnectRef, strConnectPictureRef)) {
// strConnectRef will be in the form "Body.etc..."
// the actual data will reside in "xdp.datasets.connectionData.connectionName.Body.etc..."
// note that the handler may change the value of tempioRecursingData.
// in the import case it will set it to the new parent
bContinue = handler.handleConnect(child, strConnectionRootRef.value, strConnectRef.value, strConnectPictureRef.value, handlerData, tempioRecursingData);
}
if (bContinue && eUsage == EnumAttr.USAGE_IMPORTONLY) {
// is it really a form node?
if (child instanceof FormSubform ||
child instanceof FormField ||
child instanceof FormExclGroup) {
// need to get 'real' form model since this is a static method
((FormModel)child.getModel()).setDynamicProperties((Container)child, strConnectionName, false);
}
}
if (bContinue) {
bContinue = recurseConnectOnNodeHelper(child, strConnectionName, eUsage, handler, handlerData, tempioRecursingData);
}
// is there anything to clean up now that recursion is complete
Container.FormInfo formInfo = ((Container)child).getFormInfo();
if (formInfo != null) {
((Container)child).setFormInfo(null);
}
}
if (!bContinue)
break;
}
return bContinue;
}
/** @exclude from published api. */
static void setValidationMessage(Element validateNode, String sMessage, String aType) {
TemplateModel.setValidationMessage(validateNode, sMessage, aType);
}
/**
* Determines if a SOM expression returns multiple nodes. Previously just
* checking for '*' was sufficient, but with predicate expressions we also
* have to check for .[expr] and .(expr).
*
* @param sSom
* the SOM expression to test
* @return true if the SOM expression can return multiple nodes.
* @exclude from published api.
*/
static boolean isSomMultiple(String sSom) {
// Search for [*] expressions
if (sSom.indexOf('*') != -1)
return true;
// Search for .[formcalc] expressions
if (sSom.contains(".["))
return true;
// Search for .(javascript) expressions
if (sSom.contains(".("))
return true;
return false;
}
private boolean mbWeightedData;
private boolean mbAdjustData;
private boolean mbEmptyMerge; // Flag to store if data needs is to be used during the merge
private boolean mbMergeComplete = true; // Flag to store if the merge is done.
private boolean mbAllowNewNodes; // Flag to store that the new form nodes can be created
private boolean mbExchangingDataWithServer; // Flag to indicate if we are executing a script with runAt = server.
// We don't execute initialize scripts in this case.
private boolean mbRegisterNewEvents = true; // Flag used to indicate that we don't need to register the events
// used when layout is peeking to get the overflow trailer size information
private boolean mbValidateBeforeSubmit; // Flag used to indicate if we need to validate before submitting
private boolean mbValidateBeforeExecute; // Flag used to indicate if we need to validate before executing
private DataModel mDataModel; // The Data Model
private TemplateModel mTemplateModel; // The Template Model
private DataNode mStartNode; // The data root for the merge
private final List mGlobalDataNodes = new ArrayList(); // list of global data nodes
private final List mExplicitMatchNodes = new ArrayList(); // list of Form nodes with dataRefs or globals
private int mnCalcEventId;
private int mnValidateEventId;
private int mnValidationStateEventId;
private boolean mbRecursiveIndexChange;
private boolean mbEnableIncrementalMerge = true;
private boolean mbWasIncrementalMerge;
private final List mPendingCalculateNodes = new ArrayList();
private int mnNextPendingCalculateNode;
private final List mPendingValidateNodes = new ArrayList();
private int mnNextPendingValidateNode;
private final List mNewValidateNodes = new ArrayList();
private Validate mValidate;
private Validate mDefaultValidate;
// Holds a list of XFAContainerImpl that we need to fire the validationState event
// for as soon as validation processing is complete.
private final List moValidationStateChanges = new ArrayList();
private int mnValidationRecursionDepth;
private final List mLayoutContent = new ArrayList(); // list of Form nodes that have to be removed
private Subform mRootSubform; // the current root subform
private FormSubform mRootFormSubform;
private Element mCurrentPageSet; // the current page area to append new pages to
private DataNode mDataDescription; // data description root us a wrapper to ensure it doesn't go out of scope
private String msLocale = "";
private boolean mbMatchDescendantsOnly = false; // used for dynamic merge in a pageArea
private String msSubmitURL;
private String[] mExcludeList; // a list of excluded activities
private int meRunAtSetting = EnumAttr.RUNSCRIPTS_BOTH;
private ServerExchange mServerExchange;
private Submit mSubmit;
private Execute mExecute;
private HostPseudoModel mHostPseudoModel;
private EventPseudoModel mEventPseudoModel;
// private SignaturePseudoModel mpoSignaturePseudoModel; // JavaPort: Not ported yet
private boolean mbIgnoreCalcEnabledFlag;
private boolean mbIgnoreValidationsEnabledFlag;
private String msConnectionName;
private boolean mbConnectionMerge;
private boolean mbFormStateUsage;
private boolean mbFormStateRemoved;
private boolean mbOverlayDataMergeUsage;
private int mnPanel;
private boolean mbIsXFAF;
private FormField mActiveField;
private FormField mPrevActiveField;
private FormSubform mDeltasSubform;
private boolean mbRestoreDeltas;
private boolean mbForceRestore;
private boolean mbIsCalculating;
private boolean mbDisableRemerge = false;
private boolean mbSkipCyclicAndDuplicateCheck = false;
// We support two consumption modes in the merge algorithm: global consumption
// (the legacy mode) which will only bind a non-global, non-single-dataRef binding
// to a given node once, and local consumption, which only binds once within each
// parent context. The later is needed for relational data models (among others)
// where, for instance, the list of owners for several items might include some
// of the same members.
private boolean mbGlobalConsumption = true;
/**
* Defines the callback interface that will be invoked immediately after
* a merge but before any initialize events are fired.
* @see FormModel#setPostMergeHandler(PostMergeHandler, Object)
* @see FormModel#getPostMergeHandler()
*/
public interface PostMergeHandler {
/**
* Defines the callback method that will be invoked immediately after a merge but before
* any initialize events are fired.
* @param clientData the Object passed to {@link FormModel#setPostMergeHandler(PostMergeHandler, Object)}
*/
public void handlePostMerge(Object clientData);
}
private PostMergeHandler mPostMergeHandler;
private Object mPostMergeHandlerClientData;
/**
* @exclude from published api.
*/
public FormModel(Element parent, Node prevSibling) {
super(parent, prevSibling,
STRS.XFAFORMNS_CURRENT, XFA.FORM, XFA.FORM,
STRS.DOLLARFORM, XFA.FORMTAG, XFA.FORM, getModelSchema());
}
/**
* Adds a scripting dependency between two nodes.
*
* @param node
* the Element that is dependent on some other object for
* calculation or validation
* @param dependsOn
* the object that node depends on
* @param bIsCalculate
* bIsCalculate is true if it's a calculate script, false if
* validate script
*
* @exclude from published api.
*/
void addScriptDependency(Element node, Obj dependsOn, boolean bIsCalculate) {
List listenerTable = null;
if (node instanceof FormField)
listenerTable = ((FormField)node).getFormListeners(true);
else if (node instanceof FormExclGroup)
listenerTable = ((FormExclGroup)node).getFormListeners(true);
else if (node instanceof FormSubform)
listenerTable = ((FormSubform)node).getFormListeners(true);
if (listenerTable != null) {
// add new dependency to the moFormListeners list, and add a listener to be
// notified of changes via updateFromPeer.
if (dependsOn instanceof Obj) {
FormListener listener = new FormListener(this, node, dependsOn, bIsCalculate);
listenerTable.add(listener);
}
}
}
/**
* @exclude from published api.
*/
public void addUseNode(Element useNode) {
if (isLoading() || // don't support protos on load
mbAllowNewNodes || // mbAllowNewNodes is set to true when we are merging or cloning nodes
useNode.isContainer() ||
mTemplateModel == null ||
mTemplateModel.getLegacySetting(AppModel.XFA_LEGACY_V27_SCRIPTING))
return;
super.addUseNode(useNode);
}
/**
* @exclude from published api.
* @param container
*/
void addValidationStateChanged(Container container) {
moValidationStateChanges.add(container);
}
/**
* Not supported on FormModel
* @exclude from published api.
*/
public void addUseHRefNode(Element useHRefNode) {
}
/**
* Create and/or move data matches for default bindings. Only used
* by the legacy merge algorithm, as createAndAdjustData()
* reduces the number of tree walks by one.
*
* @param formParent
* the form model Element start from
* @param dataParent
* the data model Element to start from
* @exclude from published api.
*/
private void adjustData(Element formParent, Element dataParent) {
// Search through the Form DOM starting at poFormParent looking for MATCH_ONCE form nodes which
// either don't have data nodes or have data nodes which don't match the form hierarchy.
for (Node formChild = formParent.getFirstXFAChild(); formChild != null; formChild = formChild.getNextXFASibling()) {
Element dataChild = null;
boolean bAdjustChild = true;
int eMergeType = mergeType(formChild, "", null);
switch (eMergeType) {
case EnumAttr.MATCH_ONCE: {
// We don't want to create data for leaders and trailers, but this routine should
// only get called on their children.
if (formChild instanceof FormSubform &&
((FormSubform)formChild).isLayoutNode()) {
assert(false); // JEY TODO: Actually, I don't think this ever worked becase the call
// that sets isLayoutNode() is made after postMerge() is called....
break;
}
// get the data node
if (formChild.isMapped())
dataChild = getDataNode(formChild);
if (dataChild != null) {
// Move data nodes which don't match the form hierarchy. Otherwise the globally
// consumptive merge algorithm has a hard time round-tripping. (Not that this
// entirely fixes that issue, but it helps in the simpler cases.)
if (dataParent != null) {
boolean bMove = false;
if (dataParent != dataChild.getXFAParent()) {
bMove = true;
}
// Re-sort children of an unordered subformSet to be in form order.
else if (formParent instanceof FormSubformSet &&
EnumAttr.RELATION_UNORDERED == formParent.getEnum(XFA.RELATIONTAG)) {
bMove = true;
}
if (bMove) {
// Don't move data nodes which are encoded as attributes.
if (dataChild.getClassTag() == XFA.DATAVALUETAG) {
if (((DataNode)dataChild).isAttribute())
break;
}
dataParent.appendChild(dataChild, true);
}
}
}
else {
// No match found, create an empty data node in the data DOM
if (dataParent == null)
dataChild = createDataNode((Container)formChild, mStartNode, eMergeType, false);
else
dataChild = createDataNode((Container)formChild, dataParent, eMergeType, false);
bindNodes(formChild, (DataNode)dataChild, true);
consumeDataNode(null, dataChild, FormModel.DatasetSelector.MAIN_DATASET);
}
break;
}
case EnumAttr.MATCH_DESCENDANT:
case EnumAttr.MATCH_DATAREF:
case EnumAttr.MATCH_GLOBAL:
{
// don't process child, createAndAdjustData will handle this node
bAdjustChild = false;
break;
}
case EnumAttr.MATCH_NONE:
// do nothing
break;
}
if (bAdjustChild && formChild.isContainer()) {
if (dataChild == null)
dataChild = dataParent;
// don't process exclGroups if the data node is a data value.
if (formChild instanceof ExclGroup &&
dataChild.getClassTag() == XFA.DATAVALUETAG)
continue;
// Recurse through MATCH_ONCE descendants
adjustData((Element)formChild, dataChild);
}
}
}
/**
* Helper function to permit the temporary update the mbAllowNewNodes setting.
* This should call allowNewNodes(TRUE) before cloning any form node to avoid exceptions
* @param bAllow the new value of mbAllowNewNodes
* @return the old value of mbAllowNewNodes
*/
private boolean allowNewNodes(boolean bAllow) {
boolean bOldValue = mbAllowNewNodes;
mbAllowNewNodes = bAllow;
return bOldValue;
}
/**
* Sets the value of the form node from the data node. Set the
* peering/mapping for the nodes.
*
* @param formNode
* a node from the FormModel
* @param dataNode
* a node from the DataModel
* @param bUpdateData
* if true
the value in dataNode should be updated
* using the value from formNode
* @exclude from published api.
*/
void bindNodes(Node formNode, DataNode dataNode, boolean bUpdateData /* = false */ ) {
if (dataNode == null)
return;
boolean bPeer = true;
if (mbConnectionMerge) {
Element parentDataNode = dataNode.getXFAParent();
while (parentDataNode != null && !(parentDataNode instanceof DataModel)) {
// don't set up a peer relationship if the data node is under
// the connectionData dataset
if (parentDataNode.getClassTag() == XFA.DATAGROUPTAG &&
(parentDataNode.getName() == XFA.CONNECTIONDATA)) {
bPeer = false;
break;
}
parentDataNode = parentDataNode.getXFAParent();
}
}
if (bUpdateData) {
// The CONSUME_DATA merge algorithm uses the template's default value only for the
// first binding; all others update the form node from the data.
if (mergeMode() == EnumAttr.MERGEMODE_CONSUMEDATA && dataNode.isMapped())
bUpdateData = false;
// The MATCH_TEMPLATE merge algorithm always uses the template's default value anytime
// it finds one, so that a single binding can set a default whether it happens to be
// first or not.
if (mergeMode() == EnumAttr.MERGEMODE_MATCHTEMPLATE && formNode instanceof FormField) {
FormField formField = (FormField)formNode;
Field templateField = (Field)(formField.getProto());
if (StringUtils.isEmpty(templateField.getRawValue()))
bUpdateData = false;
}
}
// check for placeholder flag - this means node was created by the dataDescription
// during the merge as a side-effect of another node and should therefore be treated as
// a newly created node needing FILL
if (dataNode instanceof DataNode &&
((DataNode)dataNode).getIsDDPlaceholder()) {
// clear place holder flag when the node is used
((DataNode)dataNode).setIsDDPlaceholder(false);
bUpdateData = true;
}
if (formNode instanceof FormField) {
((FormField)formNode).setDataNode(dataNode, bUpdateData, bPeer, "", true);
}
else if (formNode instanceof FormSubform) {
((FormSubform)formNode).setDataNode(dataNode, true);
}
else if (formNode instanceof FormExclGroup) {
((FormExclGroup)formNode).setDataNode(dataNode, bUpdateData, bPeer);
}
else {
return;
}
outputTraceMessage(ResId.FormNodeMatchedTrace, formNode, dataNode, "");
}
/** @exclude from published api. */
boolean calculationsPending() {
return mPendingCalculateNodes.size() > 0;
}
/**
* Determines whether we should queue up calcs/validations for the given node.
* Cases we watch for are:
* a) if the object is inactive we don't queue
* b) if adding the node to the moPendingCalculateNodes (if bCalculate is TRUE)
* or moPendingValidateNodes (if bCalculate is FALSE) will create cyclic dependency or
* duplicate.
*/
private boolean canBeQueued(Node node, boolean bCalculate) {
// ensure the node we are looking at is attached to the doc
if (node.getXFAParent() == null)
return false;
// Inactive objects do not fire their calculations or validations, as documented in
// https://zerowing.corp.adobe.com/display/xtg/InactivePresenceXFAProposal
// To mitigate performance on older forms, only do this check for XFA 3.0 documents and higher.
if ((mTemplateModel != null) &&
(mTemplateModel.getOriginalXFAVersion() >= Schema.XFAVERSION_30) &&
(node instanceof Container)) {
Container container = (Container)node;
int ePresence = container.getRuntimePresence(EnumAttr.UNDEFINED);
if (EnumAttr.PRESENCE_INACTIVE == ePresence)
return false;
}
List list = bCalculate ? mPendingCalculateNodes : mPendingValidateNodes;
int nStart = bCalculate ? mnNextPendingCalculateNode : mnNextPendingValidateNode;
// There are two purposes to this first loop:
//
// 1) Detect cyclic dependencies. Scripts in the list in the range from nStart
// to the end of the list are pending. Scripts from the range 0 to nStart-1
// already have been executed in the current recalculation. If we find that
// the script has been previously executed CYCLE_MAX times, just stop & don't
// queue it up again to prevent an infinite loop.
//
// 2) Search for a duplicate. Don't add it if it's already there.
// Duplicates would occur when a dependent changes for a script which is already queued
// up to run later. In this case we don't want to re-fire the later calculation
// because it's queued up to run anyway.
//
// Note: Return true if another script was queued up and false otherwise.
if (! mbSkipCyclicAndDuplicateCheck) { // Skip for performance prototyping.
int nCycleCount = 0;
for (int i = 0; i < list.size(); i++) {
if (list.get(i) == node) {
if (i < nStart) {
// It's less then nStart (meaning that the script has already been executed),
// so just keep an eye out for cyclic dependencies (i.e. calcs that depend
// on each other).
if (++nCycleCount > CYCLE_MAX)
return false; // cycle detected -- just stop
}
else {
// It's >= nStart, meaning a duplicate was found in the pending calcs.
// No need to add it again.
return false;
}
}
}
}
return true;
}
private void checkForItems(FormField field, Node dataMatch) {
// THIS IS A HACK FOR FORM SERVER!!!!!!!!!!!!
Element ui = field.getElement(XFA.UITAG, true, 0, false, false);
if (ui != null) {
// get current ui
Node currentUI = ui.getOneOfChild(true, false);
if (currentUI != null) {
if (currentUI.isSameClass(XFA.CHOICELISTTAG)) {
// if we match a value to a choice list, it is
// potentially followed by in the overlay
// data. This is a list of values to merge into the
// choicelist.
NodeList dataChildren = dataMatch.getNodes();
int nNodes = dataChildren.length();
boolean bFoundItems = false;
Node itemsDataNode = null;
for (int i = 0; i < nNodes; i++) {
itemsDataNode = (Node)dataChildren.item(i);
String sName = itemsDataNode.getName();
if (sName.compareToIgnoreCase("items") == 0) {
bFoundItems = true;
break;
}
}
if (!bFoundItems || itemsDataNode == null)
return;
consumeDataNode(null, itemsDataNode, FormModel.DatasetSelector.MAIN_DATASET);
NodeList dataItemsChildren = itemsDataNode.getNodes();
int n = dataItemsChildren.length();
Field.ItemPair itemPair = new Field.ItemPair();
// Retrieve the bound and text item lists.
field.getItemLists(false, itemPair, true);
Items displayItems = itemPair.mDisplayItems;
Items saveItems = itemPair.mSaveItems;
// okay - ready to go - clear existing list items first
if (displayItems != null)
displayItems.clearItems(false);
if (saveItems != null)
saveItems.clearItems(false);
int i = 0;
while (i < n) {
Node dataChild = (Node)dataItemsChildren.item(i);
consumeDataNode(null, dataChild, FormModel.DatasetSelector.MAIN_DATASET);
String aChildName = dataChild.getName();
if (aChildName == null) {
i++;
continue;
}
String sSaveValue = ((DataNode)dataChild).getValue();
// NOTE: this assumes either
//
// a
// A
// b
// B
// ...
//
// or
//
// a
// b
// ...
//
//
// it does not take care of multiple occurrences of
// 'display' under 'save'
i++;
Node displayChild = (Node)dataItemsChildren.item(i);
consumeDataNode(null, displayChild, FormModel.DatasetSelector.MAIN_DATASET);
String aDisplayChildName = displayChild.getName();
String sDisplayValue;
if (aDisplayChildName == XFA.SAVE)
sDisplayValue = sSaveValue;
else
sDisplayValue = ((DataNode)displayChild).getValue();
if (displayItems != null)
displayItems.addItem(sDisplayValue, false);
if (saveItems != null && saveItems != displayItems)
saveItems.addItem(sSaveValue, false);
i++;
}// endwhile
}// endif choiceList
}// endif currentUI
}// endif ui
}
/**
* Removes any leader or trailer nodes under this node.
*
* @exclude from published api.
*/
void cleanupLayoutNodes() {
// remove any static nodes from the form DOM
while (mLayoutContent.size() > 0) {
Element last = mLayoutContent.get(mLayoutContent.size() - 1).mNode;
mLayoutContent.remove(mLayoutContent.size() - 1);
if (null != last) {
// reset parents leader/trailer counts;
Node parent = last.getXFAParent();
if (parent != null && parent.getModel() != null) { // if we have a parent and that parent is valid
if (parent.isSameClass(XFA.SUBFORMSETTAG)) {
((SubformSet)parent).reset();
}
else if (parent.isSameClass(XFA.SUBFORMTAG)) {
((Subform)parent).reset();
}
// *sigh* Don't remove if null parent, else throws exception
// (which seems a little extreme to me)
last.remove();
// cleanup and data nodes tied to oLast
// also ensure that all the children have a null model, this way we
// can ensure that we don't run any calcs or validates on these
// nodes, this also removes any peers and events tied to
// this node
removeReferencesImpl(last, false);
}
}
}
mCurrentPageSet = null;
}
/**
* Removes any layout-generated nodes from under the page area.
* This will be all leader and trailer subforms.
*
* @exclude from published api.
*/
void cleanupLayoutNodes(Node node) {
// Need to retain one child ahead in the loop because removing the current
// child will also remove the link to the next sibling.
Node nextChild;
for (Node child = node.getFirstXFAChild(); child != null; child = nextChild) {
nextChild = child.getNextXFASibling();
if (child.isContainer()) {
// don't process draws or fields
if (child.isSameClass(XFA.DRAWTAG) ||
child.isSameClass(XFA.FIELDTAG))
continue;
if (child instanceof FormSubform &&
((FormSubform)child).isLayoutNode() ) {
if (node.getModel() != null) { // only need to remove the node if the parent is valid
// remove for form DOM
child.remove();
// cleanup and data nodes tied to pChild
// also ensure that all the children have a null model, this way we
// can ensure that we don't run any calcs or validates on these
// nodes, this also removes any peers and events tied to this node
removeReferences(child);
}
continue; // for loop
}
// recursive call
cleanupLayoutNodes(child);
}
}
}
/**
* Resets the focus. No field will have focus after but the form model will
* remember which field had the focus last.
*
* @exclude from published api - UI methods not relevant for server code.
*/
public void clearFocus() {
mPrevActiveField = mActiveField;
mActiveField = null;
}
/**
* Loads the serialized form DOM into mDeltas.
*/
private String computeCheckSum() {
// JavaPort: In the C++ implementation, a PKI_Base-derived object must
// be set using XFAFormModelImpl::setPKI, or the checksum is not
// calculated. The PKI_Base class is not ported since this is the only
// place where it is used at this level, and the hash function is always
// SHA-1, which is trivial to invoke directly via JCE.
ByteArrayOutputStream tempStream = new ByteArrayOutputStream();
DOMSaveOptions formStateOptions = new DOMSaveOptions();
formStateOptions.setExcludePreamble(true);
formStateOptions.setExpandElement(true);
formStateOptions.setDisplayFormat(DOMSaveOptions.RAW_OUTPUT);
// Note: don't need to canonicalize, we simply must save the template and data streams
// if there are any runtime differences then the formstate is invalid.
//
// UPDATE: not entirely true. In order to validate our checksums we have to write out
// the same thing that we're going to get when we read it back in. Since our parser
// canonicalizes the order of namespaces when reading them in, we have to at least do
// that much when writing them out. Watson 1370356
formStateOptions.setCanonicalizeNamespaceOrder(true);
// Get an instance of a SHA-1 digest function
java.security.MessageDigest md;
try {
md = java.security.MessageDigest.getInstance("SHA-1");
}
catch (java.security.NoSuchAlgorithmException ex) {
assert false;
return "";
}
// save out template DOM
mTemplateModel.saveXML(tempStream, formStateOptions);
md.update(tempStream.toByteArray());
tempStream.reset();
// save out data DOM
mDataModel.saveXML(tempStream, formStateOptions);
md.update(tempStream.toByteArray());
tempStream = null;
byte[] hash = md.digest();
return Base64.encode(hash, false);
}
private int countOverlayDataChild(Node formNode, Node dataParent) {
int nCount = 0;
for (Node dataChild = dataParent.getFirstXFAChild(); dataChild != null; dataChild = dataChild.getNextXFASibling()) {
// check if the data node and proto node are mappable
if (isMappableForOverlayData(formNode, dataChild, false))
nCount++;
}
return nCount;
}
/**
* Recursively binds the XFA Data DOM node with an XFA Template DOM node.
*
* @param templateNode
* the template node to match
* @param dataNode
* the data node to match
* @param formParent
* the parent we'll add created form nodes to
* @param connectionDataParent
* the connection data parent
*
* @exclude from published api.
*/
void createAndMatchChildren(Element templateNode,
DataNode dataNode,
Element formNode,
Element connectionDataParent) {
if (formNode.isContainer() && connectionDataParent != null) {
setConnectionDataContextInfo((Container)formNode, connectionDataParent);
}
// stop if we are not a container or if we are a leaf node
if (!formNode.isContainer() ||
formNode.isSameClass(XFA.FIELDTAG) ||
formNode.isSameClass(XFA.DRAWTAG))
return;
boolean bMatchDescendantsOnly = getMatchDescendantsOnly();
if (!bMatchDescendantsOnly) {
int eMergeType = mergeType(templateNode, "", null);
switch (eMergeType) {
case EnumAttr.MATCH_GLOBAL:
case EnumAttr.MATCH_DESCENDANT:
case EnumAttr.MATCH_DATAREF:
{
setMatchDescendantsOnly(true);
break;
}
}
}
// For each child of template prototype node
for (Node templateChild = templateNode.getFirstXFAChild(); templateChild != null; templateChild = templateChild.getNextXFASibling()) {
if (!(templateChild instanceof Element))
continue;
Element templateElement = (Element)templateChild;
// Check if this is a template node we should map
if (mapChild(templateElement, formNode)) {
// match the protoChild against the data
createAndMatchNode(templateElement, dataNode, formNode, connectionDataParent);
}
}
// reset
setMatchDescendantsOnly(bMatchDescendantsOnly);
}
// Adobe patent application tracking # P624, entitled "Form-based Data Storage And Retrieval", inventors: Matveief,Young,Solc
/**
* Recursively binds the XFA Data DOM nodes with an XFA Template DOM node.
*
* @param templateNode
* the template node to match
* @param dataParent
* the data parent to match from
* @param formParent
* the parent we'll add created form nodes to
* @param nStartDataIndex
* start index for searching
* @return the number of created nodes
*
* @exclude from published api.
*/
int createAndMatchNode(Element templateNode,
DataNode dataParent,
Element formParent,
Element connectionDataParent) {
int nNumCreated = 0;
if (templateNode instanceof Subform) {
// create instanceManager for this subform
FormInstanceManager instanceManager = createInstanceManager(templateNode, formParent);
// ensure we can create the node
int nMax = getOccurAttribute(templateNode, XFA.MAXTAG);
if ((nMax != ChildReln.UNLIMITED) && (nMax < 1))
return 0;
int nRequired = getOccurAttribute(templateNode, XFA.MINTAG);
// get the form info
Container.FormInfo formInfo = getFormInfo(templateNode, dataParent, connectionDataParent);
DataNode dataMatch = findMatch(formInfo, true, FormModel.DatasetSelector.MAIN_DATASET);
while (dataMatch != null) {
// Found a match!
nNumCreated++;
if (!formInfo.bConnectDataRef) { // the dataMatch is a new data parent
Element formNode = createFormNode(templateNode, formParent, instanceManager);
bindNodes(formNode, dataMatch, false);
consumeDataNode(formInfo, dataMatch, FormModel.DatasetSelector.MAIN_DATASET);
resetLocalConsumptionContext(templateNode);
createAndMatchChildren(templateNode, dataMatch, formNode, connectionDataParent);
}
else { // the dataMatch is the new connectionDataParent
consumeDataNode(formInfo, dataMatch, FormModel.DatasetSelector.MAIN_DATASET);
DataNode actualDataMatch = findMatch(formInfo, true, FormModel.DatasetSelector.ALT_DATASET);
if (actualDataMatch != null) {
Element formNode = createFormNode(templateNode, formParent, instanceManager);
resetLocalConsumptionContext(templateNode);
createAndMatchChildren(templateNode, actualDataMatch, formNode, dataMatch);
// push the connection data back to pre-existing data DOM nodes
bindNodes(formNode, actualDataMatch, UPDATE_DATA);
consumeDataNode(formInfo, actualDataMatch, FormModel.DatasetSelector.ALT_DATASET);
}
else {
Element formNode = createFormNode(templateNode, formParent, instanceManager);
resetLocalConsumptionContext(templateNode);
createAndMatchChildren(templateNode, dataParent, formNode, dataMatch);
}
}
// max number of objects reached stop merging
if ((nMax != ChildReln.UNLIMITED) && (nNumCreated == nMax))
return nNumCreated;
formInfo = getFormInfo(templateNode, dataParent, connectionDataParent);
dataMatch = findMatch(formInfo, true, FormModel.DatasetSelector.MAIN_DATASET);
}
// Our current subform might just be a structural or layout element, so if it has no binding
// itself, check to see if it has any children which match the data.
// Note that in legacy mode (MERGEMODE_CONSUMEDATA), we do this regardless of whether or not
// the subform has a binding, which produces some sub-optimal results.
if (formInfo.eMergeType == EnumAttr.MATCH_NONE || mergeMode() == EnumAttr.MERGEMODE_CONSUMEDATA) {
while (hasDescendantMatch(templateNode, dataParent, connectionDataParent, formInfo.eMergeType, false, null)) {
nNumCreated++;
Element formNode = createFormNode(templateNode, formParent, instanceManager);
// Note: poFormNode doesn't get bound to anything; it's just here to hold its children
// Note: we don't reset the local consumption context since we didn't bind
createAndMatchChildren(templateNode, dataParent, formNode, connectionDataParent);
// max number of objects reached stop merging
if ((nMax != ChildReln.UNLIMITED) && (nNumCreated == nMax))
break;
}
}
if (nNumCreated == 0)
nRequired = getOccurAttribute(templateNode, XFA.INITIALTAG);
// create the remaining required subforms
while (nNumCreated < nRequired) {
// A CONSUME_DATA merge uses a second pass for data creation so all we need to do here is create
// empty form nodes.
// A MATCH_TEMPLATE merge does everything in a single pass so we need to create the form and
// data nodes and recurse into our children.
//
if (mergeMode() == EnumAttr.MERGEMODE_CONSUMEDATA)
createEmptyFormNode(templateNode, formParent, connectionDataParent, instanceManager);
else {
Element formNode = createFormNode(templateNode, formParent, instanceManager);
dataMatch = null;
if (formInfo.bAssociation) {
// Someday we may create associations at runtime, but that day is not today.
}
else if (formInfo.eMergeType == EnumAttr.MATCH_NONE) {
// No data to create/bind, but we need to process children relative to parent's data:
dataMatch = dataParent;
}
else if (mapChild(templateNode, formParent)) {
dataMatch = createDataNode(formNode, dataParent, formInfo.eMergeType, true);
bindNodes(formNode, dataMatch, UPDATE_DATA);
consumeDataNode(null, dataMatch, DatasetSelector.MAIN_DATASET);
}
// Continue down with the new data context:
createAndMatchChildren(templateNode, dataMatch, formNode, connectionDataParent);
}
nNumCreated++;
}
}
else if (templateNode instanceof Field) {
FormField formNode = (FormField)createFormNode(templateNode, formParent, null);
// get the form info
Container.FormInfo formInfo = getFormInfo(templateNode, dataParent, connectionDataParent);
assert formInfo != null;
DataNode dataMatch = findMatch(formInfo, false, FormModel.DatasetSelector.MAIN_DATASET);
if (formInfo.bConnectDataRef && dataMatch != null) { // the dataMatch is a new data parent
// update the form node before creating/finding the data node
formNode.setDataNode(dataMatch, false, false, "", true);
consumeDataNode(formInfo, dataMatch, FormModel.DatasetSelector.MAIN_DATASET);
setConnectionDataContextInfo(formNode, dataMatch);
DataNode actualDataMatch = findMatch(formInfo, false, FormModel.DatasetSelector.ALT_DATASET);
if (actualDataMatch != null) {
// push the connection data back to pre-existing data DOM nodes
bindNodes(formNode, actualDataMatch, UPDATE_DATA);
consumeDataNode(formInfo, actualDataMatch, FormModel.DatasetSelector.ALT_DATASET);
}
}
else {
if (dataMatch == null) {
// We've already looked at attributes of poDataParent, but if we're MATCH_ONCE or
// MATCH_DESCENDANT then we also want to look at nested attributes.
if (formInfo.eMergeType == EnumAttr.MATCH_ONCE || formInfo.eMergeType == EnumAttr.MATCH_DESCENDANT)
dataMatch = findNestedAttrMatch(formNode, dataParent, formInfo.eMergeType);
}
// A CONSUME_DATA merge uses a second pass for data creation; a MATCH_TEMPLATE merge
// does everything in a single pass.
//
if (dataMatch != null|| mergeMode() == EnumAttr.MERGEMODE_CONSUMEDATA) {
bindNodes(formNode, dataMatch, false);
consumeDataNode(formInfo, dataMatch, DatasetSelector.MAIN_DATASET);
}
else {
dataMatch = createDataNode(formNode, dataParent, formInfo.eMergeType, true);
bindNodes(formNode, dataMatch, UPDATE_DATA);
consumeDataNode(null, dataMatch, DatasetSelector.MAIN_DATASET);
}
}
nNumCreated++;
}
else if (templateNode instanceof SubformSet) {
// start subformset
nNumCreated += mapSubformSet(templateNode, formParent, dataParent, connectionDataParent);
}
else if (templateNode instanceof ExclGroup) {
// get the form info
Container.FormInfo formInfo = getFormInfo(templateNode, dataParent, null);
if (formInfo.eMergeType == EnumAttr.MATCH_NONE) {
// ExclGroup not bound, but children might be
Element formNode = createFormNode(templateNode, formParent, null);
// Note: we don't reset the local consumption context since we didn't bind
createAndMatchChildren(templateNode, dataParent, formNode, connectionDataParent);
}
else { // once, global, dataref
DataNode dataMatch = findMatch(formInfo, false, FormModel.DatasetSelector.MAIN_DATASET);
if (dataMatch == null) {
// A CONSUME_DATA merge uses a second pass for data creation; a MATCH_TEMPLATE merge
// does everything in a single pass.
//
if (mergeMode() == EnumAttr.MERGEMODE_CONSUMEDATA)
createEmptyFormNode(templateNode, formParent, connectionDataParent, null);
else {
Element formNode = (Element)createEmptyFormNode(templateNode, formParent, connectionDataParent, null);
dataMatch = createDataNode(formNode, dataParent, formInfo.eMergeType, true);
bindNodes(formNode, dataMatch, UPDATE_DATA);
consumeDataNode(null, dataMatch, DatasetSelector.MAIN_DATASET);
}
}
else if (dataMatch.getClassTag() == XFA.DATAGROUPTAG) {
Element formNode = createFormNode(templateNode, formParent, null);
bindNodes(formNode, dataMatch, false);
consumeDataNode(formInfo, dataMatch, FormModel.DatasetSelector.MAIN_DATASET);
resetLocalConsumptionContext(templateNode);
createAndMatchChildren(templateNode, dataMatch, formNode, connectionDataParent);
}
else { // data value
Node formNode = createEmptyFormNode(templateNode, formParent, connectionDataParent, null);
bindNodes(formNode, dataMatch, false);
consumeDataNode(formInfo, dataMatch, FormModel.DatasetSelector.MAIN_DATASET);
}
}
nNumCreated++;
}
else { // everything else
Element formNode = createFormNode(templateNode, formParent, null);
// Note: formNode is a structural element which isn't itself bound to anything
// Note: we don't reset the local consumption context since we didn't bind
createAndMatchChildren(templateNode, dataParent, formNode, connectionDataParent);
nNumCreated++;
}
return nNumCreated;
}
/**
* Creates a new DataNode for a corresponding form node (i.e a form node
* with no DataNode).
*
* @param formNode
* the node to match with data node
* @param dataParent
* the node to which the new DataNode will be appended. (the
* context node if bDataRef is true)
* @param eMergeType
* the type of merge for the form node
* @param bSearchForNewlyCreatedFirst
* true if a search of newly-created nodes is required first
* @return the created DataNode
* @exclude from published api.
*/
DataNode createDataNode(Element formNode,
Element dataParent,
int eMergeType,
boolean bSearchForNewlyCreatedFirst) {
DataNode newDataNode = null;
// Not mergeable
if (eMergeType == EnumAttr.MATCH_NONE)
return null;
//#ifdef _DEBUG // JEY TODO: remove this once the new merge algorithm has a few hundred hours on it.
// don't process nodes under mapped exclgroups
// JavaPort TODO: using Assertions.isEnabled since we can't use
// pre-processor instructions here
if (Assertions.isEnabled) {
Node mappedParent = getMappedParent(formNode);
if (mappedParent instanceof FormExclGroup) {
assert(false);
return null;
}
}
//#endif
if (eMergeType == EnumAttr.MATCH_GLOBAL) {
// create global
boolean bUseDV = useDV(formNode);
newDataNode = (DataNode)resolveCreateGlobal(formNode, bSearchForNewlyCreatedFirst, bUseDV);
}
else if (eMergeType == EnumAttr.MATCH_DATAREF) {
// will bind the new data node to the field
String sRef = getDataRef(formNode, "", null);
boolean bUseDV = useDV(formNode);
// The CONSUME_DATA merge algorithm matches "foo[*]" with the correct instance of foo by
// skipping those that have already been mapped (which is handled inside resolveCreate).
//
// The MATCH_TEMPLATE merge algorithm matches based on index, and is also stricter about
// enforcing matchDescendantsOnly.
//
if (mergeMode() == EnumAttr.MERGEMODE_MATCHTEMPLATE) {
if (somIsStar(sRef) && formNode instanceof FormSubform) {
FormSubform formSubform = (FormSubform)formNode;
sRef = sRef.substring(0, sRef.length() - 3);
sRef += "[" + formSubform.getInstanceIndex(null) + "]";
}
if (getMatchDescendantsOnly() && somIsRelative(sRef) && sRef.charAt(0) != '$')
sRef = "$." + sRef;
}
newDataNode = resolveCreateDataRef(sRef, dataParent, bSearchForNewlyCreatedFirst, bUseDV);
if (newDataNode != null) {
if (!isMappable(formNode, newDataNode, false, false)) {
MsgFormatPos msg = new MsgFormatPos(ResId.FormInvalidDataRef);
msg.format(sRef);
msg.format(formNode.getName());
msg.format(newDataNode.getClassAtom());
throw new ExFull(msg);
}
}
}
else if (eMergeType == EnumAttr.MATCH_ONCE ||
eMergeType == EnumAttr.MATCH_DESCENDANT) {
// There are two template containers which impact the data hierarchy:
// Subform and Field. If they occur, create data nodes to match.
String aName = formNode.getName();
// ensure we have a name
if (aName == "")
return null;
boolean bUseDV = useDV(formNode);
if (dataParent == null) {
// no data context; can't create
}
else if (mDataDescription != null) {
// watson bug 1325319 we must escape "." chars in the name. E.G
// foo.bar must be foo\.bar
StringBuilder sTemp = new StringBuilder(aName);
int fromIndex = 0;
while (true) {
int nDot = sTemp.indexOf(".", fromIndex);
if (nDot == -1) break;
sTemp.insert(nDot, '\\');
fromIndex = nDot + 2;
}
// Look for either the next free data node (CONSUME_DATA merge) or an index-matched
// data node (MATCH_TEMPLATE merge).
//
StringBuilder sName = new StringBuilder();
if (getMatchDescendantsOnly()) // watson bug 1302087
sName.append("$.");
sName.append(sTemp);
if (mergeMode() == EnumAttr.MERGEMODE_CONSUMEDATA)
sName.append("[*]");
else
sName.append("[" + formNode.getIndex(true) + "]");
// search (data node might have already been created by another node or from the DD code)
newDataNode = resolveCreateDataRef(sName.toString(), dataParent, bSearchForNewlyCreatedFirst, bUseDV);
}
else {
if (bUseDV)
newDataNode = (DataNode)mDataModel.createNode(XFA.DATAVALUETAG, dataParent, aName, null, true);
else
newDataNode = (DataNode)mDataModel.createNode(XFA.DATAGROUPTAG, dataParent, aName, null, true);
// If we're not allowed to create data (mbAdjustData), then we need to mark this node as
// a default so it won't get saved out. (We do go ahead and create the node so that if
// multiple fields bind to it they'll all share a single value.)
if (!mbAdjustData)
newDataNode.makeDefault();
}
}
// set attr on data node
if (newDataNode != null)
outputTraceMessage(ResId.NodeCreatedTrace, newDataNode, null, "");
else if (eMergeType != EnumAttr.MATCH_NONE)
outputTraceMessage(ResId.UnmappedNode, formNode, null, "");
// Watson 1761270.
// When doing an addInstance, and adding data, a barcode that was associated with this field was not
// getting updated. This was because the data node did not notify it's parent that it was added and was
// therefore not tracked as a dependency.
// For scripts that reference data nodes, we need to send a notify peers message to the parent to
// ensure that the dependencies are updated.
if (mbMergeComplete && newDataNode != null && newDataNode.getXFAParent() != null)
newDataNode.getXFAParent().notifyPeers(Peer.CHILD_ADDED, newDataNode.getClassAtom(), newDataNode);
// set the data peer for the form node.
return newDataNode;
}
/**
* Merges a form node where there is no data node. This method will
* recursively match all containers below the supplied form node.
*
* @param templateNode
* the template node used to create the form node
* @param formParent
* the parent form node to merge the data with.
* @param connectionDataParent
* the connection data parent
* @param instanceManager
* the instanceManager for oProtoNode
*
* @exclude from published api.
*/
Element createEmptyFormNode(Element templateNode,
Element formParent,
Element connectionDataParent,
FormInstanceManager instanceManager) {
Element formNode = null;
DataNode dataNode = null;
// don't use the connect to determine merge type
int eMergeType = mergeType(templateNode, "", null);
// Check if this is a template node we should map
if (mapChild(templateNode, formParent)) {
// need to process global matches under the old merge algorithm.
// since they don't propagate instances the need to be processed
// the reason we don't don't care if adjust data is set is that
// global nodes don't create instances, so always process them so we
// get proper dynamic merge of their children
if (eMergeType == EnumAttr.MATCH_GLOBAL && mergeMode() == EnumAttr.MERGEMODE_CONSUMEDATA) {
// search for a global match, if found create and match children
dataNode = resolveGlobal(templateNode, true);
if (dataNode != null) {
// don't process if we have an exclgroup bound to a
// DV, this will be done below
if (!templateNode.isSameClass(XFA.EXCLGROUPTAG) || !dataNode.isSameClass(XFA.DATAVALUETAG)) {
formNode = createFormNode(templateNode, formParent, instanceManager);
bindNodes(formNode, dataNode, false);
consumeDataNode(null, dataNode, FormModel.DatasetSelector.MAIN_DATASET);
resetLocalConsumptionContext(templateNode);
createAndMatchChildren(templateNode, dataNode, formNode, connectionDataParent);
return formNode;
}
}
}
// create Form Node
formNode = createFormNode(templateNode, formParent, instanceManager);
}
else
return null;
// stop if we are not a container or if we are an leaf node
if (!templateNode.isContainer() ||
formNode.isSameClass(XFA.FIELDTAG) ||
formNode.isSameClass(XFA.DRAWTAG))
return formNode;
// grab choice info
boolean bChoice = false;
if (templateNode instanceof SubformSet) {
int eRelation = ((EnumValue)((SubformSet)templateNode).getAttribute(XFA.RELATIONTAG)).getInt();
if (eRelation == EnumAttr.RELATION_CHOICE)
bChoice = true;
}
boolean bMatchDescendantsOnly = getMatchDescendantsOnly();
if (!bMatchDescendantsOnly) {
switch (eMergeType) {
case EnumAttr.MATCH_GLOBAL:
case EnumAttr.MATCH_DESCENDANT:
case EnumAttr.MATCH_DATAREF:
{
setMatchDescendantsOnly(true);
break;
}
}
}
int nRequired = 1;
// For each child of template prototype node
for (Node templateChild = templateNode.getFirstXFAChild(); templateChild != null; templateChild = templateChild.getNextXFASibling()) {
if (!(templateChild instanceof Element))
continue;
Element templateElement = (Element)templateChild;
// create instanceManager for this child
FormInstanceManager newInstanceManager = createInstanceManager(templateElement, formNode);
// the number of required occurrences of this element.
nRequired = getOccurAttribute(templateElement, XFA.INITIALTAG);
// create the remaining required subforms.
for (int nCount = 0; nCount < nRequired; nCount++) {
createEmptyFormNode(templateElement, formNode, connectionDataParent, newInstanceManager);
}
if (bChoice && templateChild.isContainer()) {
// encountered the first
break;
}
}
// bind the new form node with the data node if there is one, must be done here
// to ensure the exclgroups are set properly
if (dataNode != null) {
bindNodes(formNode, dataNode, false);
consumeDataNode(null, dataNode, FormModel.DatasetSelector.MAIN_DATASET);
}
// reset
setMatchDescendantsOnly(bMatchDescendantsOnly);
return formNode;
}
/**
* Indicates whether the current merge mode is globally consumptive
* (XFAEnum::CONSUME_DATA) or not (XFAEnum::MATCH_TEMPLATE).
* @exclude from published api.
*/
public int mergeMode() {
return mbGlobalConsumption ? EnumAttr.MERGEMODE_CONSUMEDATA : EnumAttr.MERGEMODE_MATCHTEMPLATE;
}
/**
* Creates a form node based on a template node
*
* @param templateNode
* the template node used to create the form node
* @param formParent
* the parent to the new form node
* @param instanceManager
* the instanceManager associated with oProtoNode
* @return the newly created node.
*
* @exclude from published api.
*/
Element createFormNode(Element templateNode,
Element formParent,
FormInstanceManager instanceManager) {
assert(templateNode.getModel() instanceof TemplateModel);
Element newFormNode;
boolean bProtoChildren = !templateNode.isContainer();
boolean bNodeCanHaveEvents = false;
if (templateNode.isSameClass(XFA.FIELDTAG)) {
Element fieldElement = (Element)templateNode;
boolean bIsMultiSelect = false;
// validate the field
if (mTemplateModel.validateGlobalField(fieldElement)) {
Element ui = fieldElement.getElement(XFA.UITAG, true, 0, false, false);
if (ui != null) {
Node currentUI = ui.getOneOfChild(true, false);
if (currentUI != null && currentUI.isSameClass(XFA.CHOICELISTTAG)) {
EnumAttr eOpen = EnumAttr.getEnum(((Element)currentUI).getEnum(XFA.OPENTAG));
if (eOpen.getInt() == EnumAttr.MULTISELECT) {
bIsMultiSelect = true;
}
}
}
if (bIsMultiSelect)
newFormNode = new FormChoiceListField(formParent, null);
else
newFormNode = new FormField(formParent, null);
}
else {
// it is an error if there is global field and non-global field
// with the same name
throw new ExFull(new MsgFormat(ResId.XFAGlobalFieldConflictException, templateNode.getName()));
}
bProtoChildren = true;
bNodeCanHaveEvents = true;
}
else if (templateNode.isSameClass(XFA.SUBFORMTAG)) {
newFormNode = new FormSubform(formParent, null);
// Watson 1457810 - Update the root subform locale setting in the Form Model *not* the Template Model.
// If the template node is the root subform update the locale setting of the root formSubform
if (templateNode == mRootSubform) {
mRootFormSubform = (FormSubform)newFormNode;
// Adobe patent application tracking # B136, entitled "Applying locale behaviors to regions of a form", inventors: Gavin McKenzie, Mike Tardif, John Brinkman"
// Update the ambient locale if specified in the
// Configuration DOM settings.
String sLocale = getAmbientLocale();
if (!StringUtils.isEmpty(sLocale)) {
StringAttr oLocaleProp = new StringAttr(XFA.LOCALE, sLocale);
((FormSubform)newFormNode).setAttribute(oLocaleProp, XFA.LOCALETAG);
}
}
if (instanceManager != null)
instanceManager.addInstance(newFormNode, false);
bNodeCanHaveEvents = true;
}
else if (templateNode.isSameClass(XFA.SUBFORMSETTAG)) {
newFormNode = new FormSubformSet(formParent, null);
if (instanceManager != null)
instanceManager.addInstance(newFormNode, false);
}
else if (templateNode.isSameClass(XFA.EXCLGROUPTAG)) {
newFormNode = new FormExclGroup(formParent, null);
bNodeCanHaveEvents = true;
}
else {
newFormNode = (Element)createNode(templateNode.getClassTag(), formParent, "", "", true);
}
if (newFormNode != null) {
String aNodeName = templateNode.getName();
if (aNodeName != null && "" != aNodeName)
newFormNode.privateSetName(aNodeName);
outputTraceMessage(ResId.NodeCreatedTrace, newFormNode, null, "");
if (bProtoChildren)
((ProtoableNode)newFormNode).resolveProto((ProtoableNode)templateNode, false, false, false);
else
((ProtoableNode)newFormNode).setProto((ProtoableNode)templateNode);
newFormNode.makeDefault();
// process globals and data refs after for legacy merge algorithm.
if (mergeMode() == EnumAttr.MERGEMODE_CONSUMEDATA) {
int eMergeType = mergeType(templateNode, "", null);
switch (eMergeType)
{
case EnumAttr.MATCH_GLOBAL:
case EnumAttr.MATCH_DESCENDANT:
case EnumAttr.MATCH_DATAREF:
{
mExplicitMatchNodes.add(newFormNode);
break;
}
}
}
// register calculate, validate and all events. Because fields will call resolveProto we
// have to loop through its children and add events... for subform and exclgroup events the nodes
// will get created with createFormNode we have to register the events when they get created.
// watson 1109807: registration of events can be turn off. This happens when the layout
// is peeking the overflow trailer. Basically the form tree gets created (so the layout can know its size)
// but will never be attached to the form model so we shouldn't register the events.
if (mbRegisterNewEvents && (newFormNode.isSameClass(XFA.EVENTTAG) || bNodeCanHaveEvents))
registerEvents(newFormNode, XFAEVENTTYPE_ALL);
}
return newFormNode;
}
private void createFormState() {
// THIS IS A HACK FOR FORM SERVER!!!!!!!!!!!!
// Create an "$xfa.formState" private, undocumented,
// only-for-formServer packet
// Currently, it contains only the item values of
// all the choicelists on the form
//
//
//
// a Adobe
//
//
//
//
if (!getFormStateUsage())
return;
Element formState = (Element)getAppModel().locateChildByName("formState", 0);
if (formState == null) {
// Create the formState node.
AppModel appModel = (AppModel)getXFAParent();
formState = new Packet(appModel, null);
new ModelPeer((Element)appModel.getXmlPeer(), null,
null, "formState", "formState", null, formState);
}
else {
ModelPeer formStateDomPeer = (ModelPeer)((Packet)formState).getXmlPeer();
// blank out the formState packet
Node node = formStateDomPeer.getFirstXFAChild();
while (node != null) {
Node oNext = node.getNextXFASibling();
node.remove();
node = oNext;
}
}
// run through the form model and write out all choicelist items
// recurse through all children
for (Node node = getFirstXFAChild(); node != null; node = node.getNextXFASibling()) {
if (node instanceof Element)
createFormState((Element)node, formState);
}
}
private void createFormState(Element formNode, Element oFormState) {
// THIS IS PART OF A HACK FOR FORM SERVER!!!!!!!!!!!!
Document domDoc = oFormState.getOwnerDocument();
for (Node formChild = formNode.getFirstXFAChild(); formChild != null; formChild = formChild.getNextXFASibling()) {
if (formChild instanceof FormField) {
FormField field = ((FormField)formChild);
// Get the UI tag
Element ui = field.getElement(XFA.UITAG, true, 0, false, false);
if (ui != null) {
// get current ui
Node currentUI = ui.getOneOfChild(true, false);
if (currentUI != null) {
if (currentUI.isSameClass(XFA.CHOICELISTTAG)) {
// add items to formState
Field.ItemPair itemPair = new Field.ItemPair();
field.getItemLists(true, itemPair, false);
Node saveItem = itemPair.mSaveItems;
Node displayItem = itemPair.mDisplayItems;
boolean bModifiedList = false;
NodeList saveItems = new ArrayNodeList();
int nSaveItems = 0;
if (saveItem != null) {
// watson bug 1573819, only create formstate info if the list values
// live as a child of this form field and they are not considered default values
// due to Watson 1430554 we must pass in false to is default other wise we ask if
// template was a default and that always returns false
if (!saveItem.isDefault(false) && saveItem.getXFAParent() == field )
bModifiedList = true;
saveItems = saveItem.getNodes();
nSaveItems = saveItems.length();
}
NodeList displayItems = new ArrayNodeList();
int nDisplayItems = 0;
if (displayItem != null) {
// watson bug 1573819, only create formstate info if the list values
// live as a child of this form field and they are not considered default values
// due to Watson 1430554 we must pass in false to is default other wise we ask if
// template was a default and that always returns false
if (!displayItem.isDefault(false) && displayItem.getXFAParent() == field )
bModifiedList = true;
displayItems = displayItem.getNodes();
nDisplayItems = displayItems.length();
}
// if the lists haven't been modified then we don't need to save them
if (!bModifiedList)
continue;
int nValues = 0;
if ((nSaveItems != 0 && nDisplayItems != 0) && (nSaveItems != nDisplayItems)) {
// bad user!
nValues = (nSaveItems < nDisplayItems) ? nSaveItems : nDisplayItems;
}
else {
nValues = nSaveItems;
}
if (nValues == 0)
continue;
// create element under
Element newState = domDoc.createElementNS("", "state", oFormState);
// add ref attribute
// String sRef = pField.getSomName();
String sRef = field.getSOMExpression(this, false);
newState.setAttribute("", "ref", "ref", sRef);
// create element under
Element newItems = domDoc.createElementNS("", "items", newState);
// get save/display values
String sDisplay = "";
String sSave = "";
TextNode textNode;
for (int k = 0; k < nValues; k++) {
if (saveItems.item(k) instanceof TextNode) {
textNode = (TextNode)saveItems.item(k);
sSave = textNode.getValue();
}
if (displayItems.item(k) instanceof TextNode) {
textNode = (TextNode)displayItems.item(k);
sDisplay = textNode.getValue();
}
// create and elements under
Element save = domDoc.createElementNS("", "save", newItems);
new TextNode(save, null, sSave);
Element display = domDoc.createElementNS("", "display", newItems);
new TextNode(display, null, sDisplay);
}// endfor
}// endif (poCurrentUI instanceof
// JF_CLS_XFACHOICELIST)
}
}
}
else if (formNode.isContainer()) {
createFormState((Element)formChild, oFormState);
}
}// endfor
}
/**
* Creates an FormInstanceManager based on a template Subform node
*
* @param templateNode
* the template node used to create the form node
* @param formParent
* the parent to the new form node
* @return the newly created node.
*/
private FormInstanceManager createInstanceManager(Element templateNode, Element formParent) {
// XFAF only supports instance managers on "page level subforms"
if (mbIsXFAF) {
if (!(templateNode instanceof Subform) || templateNode.getXFAParent() != mRootSubform )
return null;
}
if ( ( templateNode instanceof Subform ||
templateNode instanceof SubformSet ) &&
( formParent instanceof FormSubform ||
formParent instanceof FormSubformSet ||
formParent.isSameClass(XFA.PAGEAREATAG)) ) {
FormInstanceManager formInstanceManager = new FormInstanceManager(formParent, null);
formInstanceManager.setTemplateNode((ProtoableNode)templateNode);
formInstanceManager.setMatchDescendantsOnly(getMatchDescendantsOnly());
formInstanceManager.makeDefault();
return formInstanceManager;
}
return null;
}
/**
* Creates a layout content node. This node will be an invisible child of
* the form model. So its contents can refrence other nodes in the model
* however other nodes can't reference it.
*
* @param staticContent
* the static content
* @param parent
* parent for the new node
* @return the Static form node
*/
private ProtoableNode createLayoutNode(ProtoableNode staticContent, Element parent) {
// Clone Node and added to the static content list in XFAFormModel
assert staticContent != null;
// right now this should only be called with subforms, subformSets or pageareas.
assert staticContent instanceof PageSet ||
staticContent instanceof Subform ||
staticContent instanceof SubformSet;
Element newStaticContent = importNode(staticContent, parent, !(staticContent instanceof PageSet));
if (parent != null) {
mLayoutContent.add(new LayoutContentInfo(newStaticContent));
}
if (newStaticContent instanceof FormSubform)
((FormSubform)newStaticContent).setLayoutNode();
else if (newStaticContent instanceof FormSubformSet) {
// If it's a subformset, recursively flag all subform children as 'layout' nodes
setLayoutNodes((FormSubformSet)newStaticContent);
}
return (ProtoableNode)newStaticContent;
}
/**
* Creates a leader or trailer Subform
*
* @param sReference
* SOM expression or id ref pointing to the Subform or SubformSet
* to create
* @param container
* parent context under which to create the leader/trailer
* @param bPeek
* false if the node is to be appended to this
* @return the leader or trailer
*
* @exclude from published api.
*/
Node createLeaderTrailer(String sReference, Container container, boolean bPeek) {
// watson 1109807: we don't want to register any events if we are just peeking
// nIndex passed by reference because this is the only break element that will be modified.
mbRegisterNewEvents = !bPeek;
try {
// watson 1569074 Before asking the template model to create the leader/trailer,
// be sure we are passing in the template context of the node.
// This is critical for proper SOM resolution etc.
Node containerTemplateContext = container;
ProtoableNode protoableContainer = (ProtoableNode)container;
while (protoableContainer != null &&
!(protoableContainer.getModel() instanceof TemplateModel)) {
protoableContainer = protoableContainer.getProto();
}
containerTemplateContext = protoableContainer;
assert containerTemplateContext != null;
ProtoableNode templateSF = null;
if (containerTemplateContext != null)
templateSF = mTemplateModel.createLeaderTrailer(sReference, containerTemplateContext, bPeek);
if (templateSF != null) {
// We use container here (not containerTemplateContext), since
// it is to be the parent of the new node in the form model.
Node newNode;
if (bPeek)
newNode = createLayoutNode(templateSF, null);
else
newNode = createLayoutNode(templateSF, container);
return newNode;
}
return null;
}
finally {
mbRegisterNewEvents = true;
}
}
/**
* @see Model#createNode(int, Element, String, String, boolean)
*
* @exclude from published api.
*/
@FindBugsSuppress(code="ES")
public Node createNode(int eClassTag, Element parent, String aNodeName, String aNS, boolean bDoVersionCheck) {
assert(aNodeName != null);
assert(aNS != null);
Element newFormNode = null;
// Watson bug 1099100
// only create container Form nodes during merge!
if (!mbAllowNewNodes &&
( eClassTag == XFA.FIELDTAG ||
eClassTag == XFA.SUBFORMTAG ||
eClassTag == XFA.SUBFORMSETTAG ||
eClassTag == XFA.EXCLGROUPTAG ||
eClassTag == XFA.DRAWTAG ||
eClassTag == XFA.AREATAG ||
eClassTag == XFA.PAGEAREATAG ||
eClassTag == XFA.PAGESETTAG))
return newFormNode;
assert eClassTag != XFA.XMLMULTISELECTNODETAG;
if (eClassTag == XFA.FIELDTAG) {
newFormNode = new FormField(parent, null);
}
else if (eClassTag == XFA.SUBFORMTAG) {
newFormNode = new FormSubform(parent, null);
}
else if (eClassTag == XFA.EXCLGROUPTAG) {
newFormNode = new FormExclGroup(parent, null);
}
else if (eClassTag == XFA.SUBFORMSETTAG) {
newFormNode = new FormSubformSet(parent, null);
}
else {
newFormNode = getSchema().getInstance(eClassTag, this, parent, null, bDoVersionCheck);
}
if (newFormNode != null && aNodeName != "") {
newFormNode.privateSetName(aNodeName);
}
return newFormNode;
}
private void createOverlayData(Node formNode) {
// THIS IS A HACK FOR FORM SERVER!!!!!!!!!!!!
// The overlay data written out here must be for the currentPage only.
// It will not be flat, but will be in the structure of the Form Dom.
// It will contain field values and choiceList items.
//
//
//
//
//
// 1.20$1.20
// AA Aardvark B Baboon
//
//
//
//
//
// do this when output.type=mergedXDP and destination=webClient
Element overlayDataNode = null;
// We will always have at least one data node, so we only
// need to look for overlayData if length is greater than 1
for (Node node = mDataModel.getFirstXFAChild(); node != null; node = node.getNextXFASibling()) {
if (node.getName() == "overlayData") {
overlayDataNode = (Element)node;
break;
}
}
if (overlayDataNode != null) {
// remove it and create an empty oDataListoDataListt.remove(oOverlayDataNode);
}
// create the xfa.dataSets.overlayData node
overlayDataNode = (Element)mDataModel.createNode(XFA.DATAGROUPTAG, mDataModel, "overlayData", mDataModel.getNS(), true);
// formNode should be a subform
Element panelDataNode = null;
if (formNode instanceof FormSubform) {
String aName = formNode.getName();
if (aName == "") {
// cannot create a nameless dataGroup, so just omit creating one for
// the nameless second level subform
panelDataNode = overlayDataNode;
}
else {
// create the dataGroup under overlayData that corresponds
// to the second level subform ("panel")
panelDataNode = (Element)mDataModel.createNode(XFA.DATAGROUPTAG, overlayDataNode, aName, "", true);
}
}
createOverlayData(formNode, panelDataNode);
}
private void createOverlayData(Node form, Element dataParent) {
// THIS IS A HACK FOR FORM SERVER!!!!!!!!!!!!
// Create the structured overlayData
Element newDataNode;
for (Node formChild = form.getFirstXFAChild(); formChild != null; formChild = form.getNextXFASibling()) {
String aChildName = formChild.getName();
if (formChild instanceof FormField) {
FormField field = ((FormField)formChild);
String sRawValue = field.getRawValue();
// add node, value, formatted value and items
// oNewDataNode = mpoDataModel.createElement(XFA.DATAVALUETAG, poDataParentImpl, sChildName);
// ((XFADataValueImpl*)oNewDataNode).setValue(sRaw,false);
// create the dataGroup for the field
newDataNode = (Element)mDataModel.createNode(XFA.DATAGROUPTAG, dataParent, aChildName, "", true);
// add the raw value
if (form instanceof FormChoiceListField) {
// multiselect choice list has multiple dataValues
if (formChild.isPropertySpecified(XFA.VALUETAG, true, 0)) {
Value value = (Value)((FormChoiceListField)formChild).getElement(XFA.VALUETAG, 0);
Node valueContent = value.getOneOfChild();
if (valueContent instanceof ExDataValue) {
List selectionList = new ArrayList();
Node node = ((ExDataValue)valueContent).getOneOfChild();
if (node instanceof XMLMultiSelectNode) {
XMLMultiSelectNode multiSelect = (XMLMultiSelectNode)node;
multiSelect.getValues(selectionList);
int nNumberSelected = selectionList.size();
for (int j = 0; j < nNumberSelected; j++) {
sRawValue = selectionList.get(j);
DataNode rawValue = (DataNode)mDataModel.createNode(XFA.DATAVALUETAG, newDataNode, "value", "", true);
rawValue.setValue(sRawValue, true);
}// endfor
}
}
}
}
else {
DataNode rawValue = (DataNode)mDataModel.createNode(XFA.DATAVALUETAG, newDataNode, "value", "", true);
rawValue.setValue(sRawValue, true);
}// endif multiSelectChoiceList
// possibly add the formatted value
String sFormattedValue = field.getFormattedValue();
if (!sFormattedValue.equals(sRawValue)) {
// write it out as a data value
DataNode oFormattedValue = (DataNode)mDataModel.createNode(XFA.DATAVALUETAG, newDataNode, "formattedValue", "", true);
oFormattedValue.setValue(sFormattedValue, true);
}
// look for items children
// Get the UI tag
Element ui = field.getElement(XFA.UITAG, true, 0, false, false);
if (ui != null) {
// get current ui
Node currentUI = ui.getOneOfChild(true, false);
if (currentUI != null) {
if (currentUI.isSameClass(XFA.CHOICELISTTAG)) {
// add items to overlayData
Field.ItemPair itemPair = new Field.ItemPair();
field.getItemLists(true, itemPair, false);
Node displayItem = itemPair.mDisplayItems;
Node saveItem = itemPair.mSaveItems;
NodeList saveItems;
int nSaveItems;
if (saveItem != null) {
saveItems = saveItem.getNodes();
nSaveItems = saveItems.length();
}
else {
saveItems = new ArrayNodeList();
nSaveItems = 0;
}
NodeList displayItems;
int nDisplayItems;
if (displayItem != null) {
displayItems = displayItem.getNodes();
nDisplayItems = displayItems.length();
}
else {
displayItems = new ArrayNodeList();
nDisplayItems = 0;
}
int nValues;
if ((nSaveItems != 0 && nDisplayItems != 0) && (nSaveItems != nDisplayItems)) {
// bad user!
nValues = Math.min(nSaveItems, nDisplayItems);
}
else {
nValues = nSaveItems;
}
if (nValues == 0)
continue;
// create under the field data group
Element items = (Element) mDataModel.createNode(XFA.DATAGROUPTAG, newDataNode, "items", "", true);
// get save/display values
String sDisplayValue = "";
String sSaveValue = "";
TextValue textNode;
// add save/display values to
for (int k = 0; k < nValues; k++) {
if (saveItems.item(k) instanceof TextNode) {
textNode = (TextValue)saveItems.item(k);
sSaveValue = textNode.getValue();
}
if (displayItems.item(k) instanceof TextNode) {
textNode = (TextValue)displayItems.item(k);
sDisplayValue = textNode.getValue();
}
DataNode save = (DataNode)mDataModel.createNode(XFA.DATAVALUETAG, items, "save", "", true);
((DataNode)save).setValue(sSaveValue, true);
DataNode display = (DataNode)mDataModel.createNode(XFA.DATAVALUETAG, items, "display", "", true);
((DataNode)display).setValue(sDisplayValue, true);
}// endfor
}// endif choiceList
}
}// endif ui
}
else if (formChild instanceof FormExclGroup) {
FormExclGroup group = ((FormExclGroup)formChild);
// add node
newDataNode = (Element) mDataModel.createNode(XFA.DATAGROUPTAG, dataParent, aChildName, "", true);
// add the raw value
String sRawValue = group.getRawValue();
DataNode rawValue = (DataNode)mDataModel.createNode(XFA.DATAVALUETAG, newDataNode, "value", "", true);
rawValue.setValue(sRawValue, true);
}
else if (formChild instanceof FormSubform) {
if (aChildName == "") {
newDataNode = dataParent;
}
else {
// add node, and recurse
newDataNode = (Element) mDataModel.createNode(XFA.DATAGROUPTAG, dataParent, aChildName, "", true);
}
createOverlayData(formChild, newDataNode);
}
}
}
/**
* Notifies the FormModel that a new page is being created so that it can take
* appropriate action.
*
* @param page
* the template pageArea node
*
* TODO: Change return type to FormPageArea.
*
* @exclude from published api.
*/
PageArea createPage(PageArea page) {
assert (mCurrentPageSet != null);
return (PageArea)importNode(page, mCurrentPageSet, true);
}
/**
* Notifies the FormModel that a new page is being created so that it can
* take appropriate action.
*
* @param oPage -
* the xfatemplate node
* @return Imported copy of given pageArea that resides in a pageset under
* the root subform.
*
* TODO: Change return type to FormPageArea.
*
* @exclude from published api.
*/
PageSet createPageSet(PageSet pageSet) {
Node pageSetParent = pageSet.getXFAParent();
Element parent = mCurrentPageSet;
// first page set added, use the root subform as the parent of the pageset
if (parent == null)
parent = mRootFormSubform;
// find the proper level to create the new pageSet.
while (parent instanceof PageSet) {
// found common parent
if (((PageSet)parent).getProto() == pageSetParent)
break;
// move up our pagesets
parent = parent.getXFAParent();
}
assert(parent.isSameClass(pageSetParent));
mCurrentPageSet = createLayoutNode(pageSet, parent);
return (PageSet)mCurrentPageSet; // JavaPort: this looks questionable
}
/**
* Creates an overlayData section under dataSets.
* This method is for Form Server support.
*
* @param nPanel
* the panel to create the overlayData section for
* @exclude from published api.
*/
public void createPanelOverlayData(int nPanel) {
// THIS IS A HACK FOR FORM SERVER!!!!!!!!!!!!
// If the output type is mergedXDP and the destination is webClient,
// output overlay data for the second level subform for caching
// purposes.
// find the second level subform that is the currentPage
Node topSubform = getFirstXFAChild();
Node targetSubform = null;
int nCnt = 0;
for (Node child = topSubform.getFirstXFAChild(); child != null; child = child.getNextXFASibling()) {
if (child instanceof FormSubform) {
if (nCnt == nPanel) {
targetSubform = child;
break;
}
nCnt++;
}
}
if (targetSubform != null) {
createOverlayData(targetSubform);
}
}
private void doBindItems(Element bindItems, FormField field, String sRequestConnectionName) {
String sConnection = "";
Attribute connection = bindItems.getAttribute(XFA.CONNECTIONTAG, true, false);
if (connection != null)
sConnection = connection.toString();
String sRef = "";
Attribute ref = bindItems.getAttribute(XFA.REFTAG, true, false);
if (ref != null)
sRef = ref.toString();
if (sRef.length() != 0 && sConnection.length() != 0) {
// it's a wsdl bindItems
if (sRequestConnectionName.equals(sConnection)) { // looking for wsdl
// if it's a wsdl data merge then the connection data is transient so there's no
// point listening to it.
field.setItemsDataListener(null);
field.updateItemsFromData(bindItems, true);
}
}
else if (sRef.length() == 0|| sConnection.length() == 0) {
// default data if sRef or database(sourceset) if sConnection
if (sRequestConnectionName.length() == 0) { // not looking for wsdl
FormItemsDataListener listener = new FormItemsDataListener(field, bindItems);
field.setItemsDataListener(listener);
field.updateItemsFromData(bindItems, false);
}
}
}
/**
* Load a node from one DOM into another.
* In the C++ implementation, this would load a node from the XML DOM into the XFA DOM.
* In this implementation, this method exists to handle the case of a form packet used
* to restore state that has been parsed into a generic packet, but now needs to be loaded
* into an XFA FormModel DOM.
* @param parent the XFA Form parent of the node that is to be loaded
* @param node the generic packet node that is to be loaded
* @param genTag
* @return the loaded node
* @exclude from published api.
*/
protected Node doLoadNode(Element parent, Node node, Generator genTag) {
assert node instanceof Element || node instanceof Chars;
// Check if we are dealing with ExData.
if (parent instanceof ExDataValue && node instanceof Element) {
boolean bCreate = false;
int eNewNodeTag = XFA.RICHTEXTNODETAG;
// Check the namespace of the child element to see if it's equal
// to the xhtml namespace.
if (((Element)node).getNS() == STRS.XHTMLNS) {
bCreate = true;
}
else {
// watson bug 1856188, since the