com.adobe.xfa.form.FormInstanceManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
The newest version!
/*
* 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.
*/
package com.adobe.xfa.form;
import com.adobe.xfa.AppModel;
import com.adobe.xfa.ScriptTable;
import com.adobe.xfa.ScriptHandler;
import com.adobe.xfa.XFAList;
import com.adobe.xfa.Int;
import com.adobe.xfa.ProtoableNode;
import com.adobe.xfa.ut.Peer;
import com.adobe.xfa.Model;
import com.adobe.xfa.EnumAttr;
import com.adobe.xfa.Element;
import com.adobe.xfa.Node;
import com.adobe.xfa.ut.FindBugsSuppress;
import com.adobe.xfa.ut.MsgFormatPos;
import com.adobe.xfa.ut.ExFull;
import com.adobe.xfa.XFA;
import com.adobe.xfa.ut.ResId;
import com.adobe.xfa.template.containers.Container;
import com.adobe.xfa.template.containers.Subform;
import com.adobe.xfa.data.DataNode;
import java.util.ArrayList;
import java.util.List;
/**
* @exclude from public api.
*/
public class FormInstanceManager extends ProtoableNode {
private final List mInstances = new ArrayList();
private ProtoableNode mTemplateNode;
private boolean mbMatchDescendantsOnly;
private InstanceListener mListener;
// state member var, records when we trigger initialize event on newly added instance.
private boolean mbInitializingNewInstance;
public FormInstanceManager(Element parent, Node prevSibling) {
super(parent, prevSibling, "", XFA.INSTANCEMANAGER, XFA.INSTANCEMANAGER, null, XFA.INSTANCEMANAGERTAG, XFA.INSTANCEMANAGER);
}
// // ~XFAFormInstanceManagerImpl() {
// void dispose() {
// mListener = null;
//
// mbInitializingNewInstance = false;
//
// // clear the instance manager for all nodes this manager controls.
// for (int i = 0; i < mInstances.size(); i++) {
// Object instance = mInstances.get(i);
//
// if (instance instanceof FormSubform) {
// // set the instanceManager for poInstance to null
// FormSubform formSubform = (FormSubform)instance;
// formSubform.setInstanceManager(null);
// }
// else if (instance instanceof FormSubformSet) {
// // set the instanceManager for poInstance to null
// FormSubformSet formSubformSet = (FormSubformSet)instance;
// formSubformSet.setInstanceManager(null);
// }
// }
// }
/**
* Add or remove instances of an Subform or SubformSet child of this
* XFAForm node.
*
* This method will automatically ensure that no occurrence or
* hierarchy rules are invalidated. During a remove data nodes are
* unmapped and during an add data is automatically merged into the
* new instances. This method will also recursively add/remove the
* children of the new node.
*
* @param nNumber the number of instances of this subform that we want to have
* @param bThrow if true
, an exception will be throw if nNumber
* is not a valid number of occurrences.
*/
public void setInstances(int nNumber, boolean bThrow /* = true */) {
// get the max occurrence info
int nMax = getMax();
FormModel formModel = (FormModel)getModel();
if (formModel.mergeMode() == EnumAttr.MERGEMODE_MATCHTEMPLATE) {
// V3.1 doesn't allow instance management in MATCH_TEMPLATE merge mode. In future versions,
// we'll likely implement some sort of incremental merge to correctly handle some of the edge
// cases of instance management (such as creating an instance that another template node
// would have bound to).
if (bThrow)
throw new ExFull(ResId.InstanceManagmentNotAllowed);
else
return;
}
// ensure the number given is valid
if (nNumber < 0) {
if (bThrow)
throw new ExFull(new MsgFormatPos(ResId.OccurrenceViolationException, XFA.MIN));
else
return;
}
if ((nNumber > nMax) && (nMax != -1)) {
if (bThrow)
throw new ExFull(new MsgFormatPos(ResId.OccurrenceViolationException, XFA.MAX));
else
return;
}
else if (nNumber < getMin()) {
if (bThrow)
throw new ExFull(new MsgFormatPos(ResId.OccurrenceViolationException, XFA.MIN));
else
return;
}
// if number is bigger than current number of instances create new ones.
if (nNumber > getCount()) {
// find where "this" occurs
int nManagerIndex = getIndex();
// the number to create
nNumber = nNumber - getCount();
for (int i = 0; i < nNumber; i++) {
// will notify of index change
addChild(nManagerIndex, getCount(), true);
}
}
// else remove instances.
else if (nNumber < getCount()) {
// the number to remove
nNumber = getCount() - nNumber;
// remove
for (int i = 0; i < nNumber; i++) {
removeInstance(mInstances.size() - 1, false);
}
}
// make all nodes non-default
clearDefaults();
// notify peers so dependency tracking can work
notifyPeers(Peer.UPDATED, "", null);
}
/**
* Add an instance of a Subform or SubformSet to this Form node.
*
* This method will automatically ensure that no occurrence or
* hierarchy rules are invalidated. This method will also
* recursively add the children of the new node.
*
* @param bMerge if true attempt to merge the new Form node against the
* XFA-Data DOM.
* @return a null Node if no node was added, else the newly created
* Form node.
*/
public Node addInstance(boolean bMerge /* = true */) {
return addInstance(getCount(), bMerge);
}
Node addInstance(int nInstance, boolean bMerge /* = true */) {
// get the max occurrence info
int nMax = getMax();
Node bRet = null;
FormModel formModel = (FormModel)getModel();
if (formModel.mergeMode() == EnumAttr.MERGEMODE_MATCHTEMPLATE) {
// V3.1 doesn't allow instance management in MATCH_TEMPLATE merge mode. In future versions,
// we'll likely implement some sort of incremental merge to correctly handle some of the edge
// cases of instance management (such as creating an instance that another template node
// would have bound to).
throw new ExFull(ResId.InstanceManagmentNotAllowed);
}
if (nInstance > getCount())
throw new ExFull(ResId.IndexOutOfBoundsException);
// ensure if we add a new instance do we respect the max occur info
if ((nMax == -1) || ((int) nMax > getCount())) {
// find where "this" occurs
int nManagerIndex = getIndex();
// this will send index Changed event
bRet = addChild(nManagerIndex, nInstance, bMerge);
} else {
throw new ExFull(new MsgFormatPos(ResId.OccurrenceViolationException, XFA.MAX));
}
// make all nodes non-default
clearDefaults();
// send notification that calcs can fire
notifyPeers(Peer.UPDATED, "", null);
return bRet;
}
void addInstance(Node instance, boolean bValidate /* = false */) {
// Note: while this routine appears to be part of instance management (and should therefore be protected by
// a check for poFormModel->mergeMode() == XFAEnum::MERGEMODE_MATCHTEMPLATE, it's actually part of the merge
// algorithm (it's simply adding newly-merged nodes to the instance manager, not creating new instances).
if (bValidate) {
int nMax = getMax();
// if not infinity and already at max issue error.
if ((nMax != -1) && ((int) nMax < getCount()))
throw new ExFull(new MsgFormatPos(ResId.OccurrenceViolationException, XFA.MAX));
}
if (instance instanceof FormSubform) {
// set the instanceManager for poInstance
// and add a pointer to the poInstance to this
FormSubform formSubformInstance = (FormSubform) instance;
formSubformInstance.setInstanceManager(this);
mInstances.add(formSubformInstance);
} else if (instance instanceof FormSubformSet) {
// set the instanceManager for poInstance
// and add a pointer to the poInstance to this
FormSubformSet formSubformSetInstance = (FormSubformSet) instance;
formSubformSetInstance.setInstanceManager(this);
mInstances.add(formSubformSetInstance);
}
}
/**
* Remove a Subform or SubformSet from this Form node.
*
* This method will automatically ensure that no occurrence rules
* are invalidated and the data is removed from the XFA-Data DOM.
*
* @param nInstance the instance to remove.
*/
public void removeInstance(int nInstance, boolean bNotifyPeers /* = true */) {
// ensure nInstance is valid
if (nInstance >= getCount())
throw new ExFull(ResId.IndexOutOfBoundsException);
// ensure if we remove a node we don't violate the min occur info
if ((getCount() - 1) < getMin())
throw new ExFull(new MsgFormatPos(
ResId.OccurrenceViolationException, XFA.MIN));
FormModel formModel = (FormModel)getModel();
if (formModel.mergeMode() == EnumAttr.MERGEMODE_MATCHTEMPLATE) {
// V3.1 doesn't allow instance management in MATCH_TEMPLATE merge mode. In future versions,
// we'll likely implement some sort of incremental merge to correctly handle some of the edge
// cases of instance management (such as creating an instance that another template node
// would have bound to).
throw new ExFull(ResId.InstanceManagmentNotAllowed);
}
// get the instance
Element instance = mInstances.get(nInstance);
// notify a listener, if any that a node is about to be removed
Node removedFormNode = instance;
if (null != mListener) {
mListener.preRemoveInstance(removedFormNode);
}
// force the removal any script object pointers to this form node
// by default the script object will keep the node alive
List handlers = getAppModel().getScriptHandlers();
for (int i = 0; i < handlers.size(); i++) {
handlers.get(i).removeReference(removedFormNode);
}
// removes the nodes
// watson bug 1705701, if this is a legacy doc once we find a data node
// stop removing data nodes
// watson bug 1440694 old 7.0 forms don't respect the override attribute,
// must continue to respect this
boolean bLegacy = getAppModel().getLegacySetting(AppModel.XFA_LEGACY_V27_SCRIPTING);
cleanDataNodes(instance, bLegacy); // this removes the data listeners and data nodes
// null out the instance manager so this.cleanup(node isn't called);
if (instance instanceof FormSubform) {
// set the instanceManager for poInstance to null
FormSubform formSubformInstance = (FormSubform) instance;
formSubformInstance.setInstanceManager(null);
} else if (instance instanceof FormSubformSet) {
// set the instanceManager for poInstance to null
FormSubformSet formSubformSetInstance = (FormSubformSet) instance;
formSubformSetInstance.setInstanceManager(null);
}
// Watson 1101722- the next line will decrement ref count of poInstance,
// causing crash when we pass it to listener.
// So keep it around with the local variable oRemovedFormNode int enough to
// inform any listener of the post-remove event
instance.remove();
mInstances.remove(nInstance);
// notify a listener, if any, that a node was just removed
if (null != mListener) {
mListener.postRemoveInstance(removedFormNode);
}
if (bNotifyPeers) {
// send index changed event to all affected nodes
if (nInstance < getCount())
broadcastIndexChange(nInstance, getCount() - 1, true);
// make all nodes non-default
clearDefaults();
// notify peers so dependency tracking can work
notifyPeers(Peer.UPDATED, "", null);
}
// 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
Model model = getModel();
if (model != null)
model.removeReferences(instance);
}
/**
* Move a child of this Form node and the corresponding data in the
* XFA-Data DOM.
*
* This method will automatically ensure that no occurrence or
* hierarchy rules are invalidated.
*
* @param nInstance the instance to be moved.
* @param nPosition the position of the child within its instances (0 based).
*/
public void moveInstance(int nInstance, int nPosition, boolean bNotify /* = true */) {
FormModel formModel = (FormModel)getModel();
if (formModel.mergeMode() == EnumAttr.MERGEMODE_MATCHTEMPLATE) {
// V3.1 doesn't allow instance management in MATCH_TEMPLATE merge mode. In future versions,
// we'll likely implement some sort of incremental merge to correctly handle some of the edge
// cases of instance management (such as creating an instance that another template node
// would have bound to).
throw new ExFull(ResId.InstanceManagmentNotAllowed);
}
if (nInstance == nPosition)
return;
// invalid position
if (getCount() < 2 ||
nPosition < 0 || nPosition >= getCount() ||
nInstance < 0 || nInstance >= getCount())
throw new ExFull(ResId.IndexOutOfBoundsException);
int nMax = getMax();
// can't move;
if (nMax == 0 || nMax == 1)
throw new ExFull(new MsgFormatPos(ResId.OccurrenceViolationException, XFA.MAX));
// get instance
Element instance = mInstances.get(nInstance);
// notify a listener, if any, that a node is just about to be moved
Node movedFormNode = instance;
if (null != mListener) {
mListener.preMoveInstance(movedFormNode);
}
// remove the instance link
mInstances.remove(nInstance);
Element parent = getXFAParent();
Node refNode = null;
int nRefIndex;
// try to get ref node form moInstances
if (getCount() > nPosition) {
refNode = mInstances.get(nPosition);
// re-add the instance to the list
mInstances.add(nPosition, instance);
} else { // getCount() == nPosition
nRefIndex = getIndex() + getCount() + 2;
int nCount = 0;
for (Node child = parent.getFirstXFAChild(); child != null; child = child.getNextXFASibling(), nCount++) {
if (nCount == nRefIndex) {
refNode = child;
break;
}
}
// re-add the instance to the list
mInstances.add(instance);
}
// if no reference node so just append
if (refNode == null)
parent.appendChild(instance, false);
else
parent.insertChild(instance, refNode, false);
// notify a listener, if any, that a node was just moved
if (null != mListener) {
mListener.postMoveInstance(movedFormNode);
}
moveDataNodes(nInstance, nPosition);
if (bNotify) {
// watson bug 1791382, for new docs only send the index changed event to nodes
// affected by the move opp.
boolean bUseRange = !getAppModel().getLegacySetting(AppModel.XFA_LEGACY_V27_SCRIPTING);
// send index changed event to the two affected nodes
broadcastIndexChange(nInstance, nPosition, bUseRange);
// make all nodes non-default
clearDefaults();
// notify peers so dependency tracking can work
notifyPeers(Peer.UPDATED, "", null);
}
}
// revert moveDataNodes to 7.0.8 codebase to ensure backwards compatibility
private void addDataNodes(int nIndex) {
// watson bug 1561530, don't move data that is bound to a schema since
// the data creation code handles this.
// #if 0
// XFAFormModelImpl * poFormModel = (XFAFormModelImpl *)getModelImpl();
//
// XFANodeImpl* poInstance = moInstances[nIndex];
//
// XFAEnum::VALUE eType = poFormModel->mergeType(poInstance);
// if (eMergeType == XFAEnum::MATCH_ONCE || eMergeType == XFAEnum::MATCH_DESCENDANT)
// {
// }
//
// getDataNodes(poInstance,oDataNodes);
//
// #endif
}
/**
* Interface for being notified of instances being added/removed/moved
*
* @exclude from public api.
*/
public interface InstanceListener {
// Notification just before instance is added/removed/moved
void preAddInstance();
void preRemoveInstance(Node toBeRemoved);
void preMoveInstance(Node toBeMoved);
// Notification just after instance is added/removed/moved
void postAddInstance(Node wasAdded);
void postRemoveInstance(Node wasRemoved);
void postMoveInstance(Node wasMoved);
}
/**
* Register an instance listener to this manager. This listener will be
* notified of instances being added/removed/moved
*/
public void setInstanceListener(InstanceListener listener) {
mListener = listener;
}
void cleanup(Node node) {
// safety first: remove this subform from the manager
// note: the manager should be freed prior to the destruction of any
// instance 99% of the time
for (int i = 0; i < mInstances.size(); i++) {
Element poInstance = mInstances.get(i);
if (node == poInstance) {
mInstances.remove(i);
break;
}
}
}
int getCount() {
return mInstances.size();
}
int getMin() {
// get the min occurrence setting
Element occur = getElement(XFA.OCCURTAG, 0);
Int min = (Int)occur.getAttribute(XFA.MINTAG);
return min.getValue();
}
int getMax() {
// get the max occurrence setting
Element occur = getElement(XFA.OCCURTAG, 0);
Int max = (Int)occur.getAttribute(XFA.MAXTAG);
return max.getValue();
}
void setTemplateNode(ProtoableNode templateNode) {
// construct the name for this instanceManager
String aName = templateNode.getName();
aName = aName == "" ? "_" : ("_" + aName).intern();
privateSetName(aName);
mTemplateNode = templateNode;
ProtoableNode occur = (ProtoableNode)templateNode.getElement(XFA.OCCURTAG, true, 0, false, false);
// copy the occur element over
if (occur != null) {
occur.createProto(this, false);
}
}
int findIndex(Node instance) {
for (int i = 0; i < mInstances.size(); i++) {
if (mInstances.get(i) == instance)
return i;
}
return 0;
}
private void clearDefaults() {
for (int i = 0; i < mInstances.size(); i++) {
mInstances.get(i).makeNonDefault(false);
}
}
private Node addChild(int nManagerIndex, int nInsertIndex, boolean bMerge) {
// Watson 1124534: Infinite loop caused when subform initialize event uses instance manger to create another instance of itself.
// Well the new instance was added already, but we avoid adding new instance if we detect this case
// Granted this is just bad form design but we'll protect the user from themselves.
if (mbInitializingNewInstance) {
// bad form design - warn of recursive or cyclic initialize event scripts
MsgFormatPos formatPos = new MsgFormatPos(ResId.XFAAddInstanceRecursiveException);
Node node = getTemplateNode();
if (null != node)
formatPos.format(node.getName());
throw new ExFull(formatPos);
}
FormModel formModel = (FormModel)getModel();
// get the parent node
Element parent = getXFAParent();
// notify a listener, if any that instance is about to be added
if (null != mListener) {
mListener.preAddInstance();
}
Node mappedParent;
if (parent.isMapped())
mappedParent = parent;
else
mappedParent = FormModel.getMappedParent(parent);
boolean bMatchDescendantsOnly = formModel.getMatchDescendantsOnly();
formModel.setMatchDescendantsOnly(getMatchDescendantsOnly());
// TODO calc if in ExplictScope
DataNode dataParent = FormModel.getDataNode(mappedParent);
Element newFormNode = null;
formModel.setAllowNewNodes(true);
if (!bMerge) {
// load empty form dom
formModel.setEmptyMerge(true);
// 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, which happens below in the
// "if (poNewFormNode == NULL)" block.
//
if (formModel.mergeMode() == EnumAttr.MERGEMODE_CONSUMEDATA)
newFormNode = formModel.createEmptyFormNode(getTemplateNode(), parent, null, this);
}
else {
// try to merge data that exists in the data dom.
formModel.setEmptyMerge(false);
if (getTemplateNode() instanceof Subform) {
Container.FormInfo info = formModel.getFormInfo(getTemplateNode(), dataParent, null);
DataNode matched = formModel.findMatch(info, true, FormModel.DatasetSelector.MAIN_DATASET);
if (matched != null) {
newFormNode = formModel.createFormNode(getTemplateNode(), parent, this);
formModel.bindNodes(newFormNode, matched, false);
formModel.consumeDataNode(null, matched, FormModel.DatasetSelector.MAIN_DATASET);
formModel.createAndMatchChildren(getTemplateNode(), matched, newFormNode, null);
}
else if (info != null &&
(info.eMergeType == EnumAttr.MATCH_GLOBAL ||
info.eMergeType == EnumAttr.MATCH_DATAREF)) {
// 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, which happens below
// in the "if (poNewFormNode == NULL)" block.
//
if (formModel.mergeMode() == EnumAttr.MERGEMODE_CONSUMEDATA)
newFormNode = formModel.createEmptyFormNode(getTemplateNode(), parent, null, this);
}
}
if (newFormNode == null) {
newFormNode = formModel.createFormNode(getTemplateNode(), parent, this);
if (formModel.mergeMode() == EnumAttr.MERGEMODE_MATCHTEMPLATE) {
int eMergeType = formModel.mergeType(getTemplateNode(), "", null);
DataNode dataNode = formModel.createDataNode(newFormNode, dataParent, eMergeType, true);
formModel.bindNodes(newFormNode, dataNode, true);
formModel.consumeDataNode(null, dataNode, FormModel.DatasetSelector.MAIN_DATASET);
dataParent = dataNode;
}
formModel.createAndMatchChildren(getTemplateNode(), dataParent, newFormNode, null);
}
}
formModel.setAllowNewNodes(false);
// move node into correct position
Node refNode = null;
int nRefIndex = nManagerIndex + getCount();
if (parent.getXFAChildCount() > nRefIndex + 1)
refNode = parent.getXFAChild(nRefIndex);
if (refNode != null)
parent.insertChild(newFormNode, refNode, true);
formModel.mergeSecondPass(parent, dataParent);
// watson 1432253 set dynamic properties follows mergeSecondPass as in normal
// merge case
if (newFormNode instanceof Container)
formModel.setDynamicProperties((Container) newFormNode, "", true);
// watson bug 1222926 ensure the new data node is in the correct place
addDataNodes(nInsertIndex);
// reset
formModel.setMatchDescendantsOnly(bMatchDescendantsOnly);
// notify a listener, if any that instance was just added
if (null != mListener) {
Node oNewFormNode = newFormNode;
mListener.postAddInstance(oNewFormNode);
}
// move node into correct position
moveInstance(getCount() - 1, nInsertIndex, false);
// Watson 1124534: Infinite loop caused when subform initialize event
// uses instance manger to create another instance of itself.
// Well the new instance was added already, but we can avoid calling
// initialize event again if we detect this case/
// Granted this is just bad form design but we'll protect the user from
// themselves.
assert (!mbInitializingNewInstance);
if (!mbInitializingNewInstance) {
// Set flag indicating we are calling initialize on new instance.
mbInitializingNewInstance = true;
try {
String sInitialize = EnumAttr.getString(EnumAttr.ACTIVITY_INITIALIZE);
formModel.eventOccurred(sInitialize, newFormNode);
} finally {
mbInitializingNewInstance = false;
}
}
// send index changed event to the two affected nodes
if (getCount() > 0) // Watson 1894170 -- ensure that script associated with ACTIVITY_INITIALIZE event didn't remove instance.
broadcastIndexChange(getCount() - 1, nInsertIndex, true);
assert (!mbInitializingNewInstance);
return newFormNode;
}
private static void insertNodeAfter(Node newNode, Node prevRefNode) {
// insert newNode after prevRefNode
Element parent = prevRefNode.getXFAParent();
if (parent == null)
throw new ExFull(ResId.InsertFailedException);
Node refNode = prevRefNode.getNextXFASibling();
if (refNode != null) {
// only insert the child if needed
if (newNode != refNode)
parent.insertChild(newNode, refNode, true);
} else
parent.appendChild(newNode, true);
}
// revert moveDataNodes to 7.0.8 codebase to ensure backwards compatibility
@FindBugsSuppress(code="ES")
private void moveDataNodes(int nFrom, int nTo) {
// MoveDataNodes
Element instance = mInstances.get(nTo);
//Node poMappedParent = FormModel.getMappedParent(instance);
//Node poDataParent = FormModel.getDataNode(poMappedParent);
List dataNodes = new ArrayList();
List prevRefDataNodes = new ArrayList();
List postRefDataNodes = new ArrayList();
getDataNodes(instance, dataNodes);
final int nNodeSet = dataNodes.size();
boolean bGiveWarning = false;
// verify and move data
if (nTo > 0) {
// previous instance
Element refNode = mInstances.get(nTo - 1);
getDataNodes(refNode, prevRefDataNodes);
final int nPrevSet = prevRefDataNodes.size();
// if both have 1 data node, all is ok, just move the node
if (nNodeSet == nPrevSet &&
nNodeSet == 1) {
insertNodeAfter(dataNodes.get(0), prevRefDataNodes.get(0));
return;
}
// the preceding instance has less data nodes, assume that it isn't round trippable
if (nNodeSet > nPrevSet)
bGiveWarning = true;
boolean bFound = false;
for (int i = nNodeSet; i > 0; i--) {
Element node = dataNodes.get(i - 1);
for (int j = nPrevSet; j > 0; j--) {
Element node2 = prevRefDataNodes.get(j - 1);
if (node2.isSameClass(node) &&
node.getName() == node2.getName()) {
bFound = true;
// insert pNode after pNode2
insertNodeAfter(node, node2);
break;
}
}
// no exact match so can't round trip
if (!bFound)
bGiveWarning = true;
}
}
// verify that the next instance is valid, move data if we are the first instance
if (nTo + 1 < getCount()) {
// next instance
Element refNode = mInstances.get(nTo + 1);
getDataNodes(refNode, postRefDataNodes);
final int nPostSet = postRefDataNodes.size();
// if both have 1 data node, all is ok, just move the node
if (nNodeSet == nPostSet &&
nNodeSet == 1 &&
nTo == 0) { // moved the node to the first position so weed to move the data here
postRefDataNodes.get(0).getXFAParent().insertChild(
dataNodes.get(0),
postRefDataNodes.get(0), true);
return;
}
// the proceeding instance has more data nodes, assume that it isn't round trippable
if (nNodeSet < nPostSet)
bGiveWarning = true;
for (int i = 0; i < nNodeSet; i++) {
Element node = dataNodes.get(i);
for (int j = 0; j < nPostSet; j++) {
Element node2 = postRefDataNodes.get(j);
if (node2.isSameClass(node) &&
node.getName() == node2.getName()) {
if (nTo == 0) {
// insert node before node2
node.getXFAParent().insertChild(node, node2, true);
break;
}
}
}
}
}
if (bGiveWarning) {
// warning that data won't roundtrip
MsgFormatPos msgFormatPos = new MsgFormatPos(ResId.XFAMoveInstanceException);
msgFormatPos.format(instance.getClassAtom());
msgFormatPos.format(instance.getName());
msgFormatPos.format(Integer.toString(nFrom));
msgFormatPos.format(Integer.toString(nTo));
throw new ExFull(msgFormatPos);
}
}
private void getDataNodes(Node formNode, List dataNodes) {
boolean bGetChildren = false;
DataNode dataNode = null;
if (formNode instanceof FormSubform) {
bGetChildren = true;
dataNode = ((FormSubform)formNode).getDataNode();
} else if (formNode instanceof FormSubformSet) {
bGetChildren = true;
} else if (formNode instanceof FormField) {
dataNode = ((FormField) formNode).getDataNode();
} else if (formNode instanceof FormExclGroup) {
bGetChildren = true;
dataNode = ((FormExclGroup) formNode).getDataNode();
}
// add data node to list
// don't add orphaned data nodes (watson bug 1335755) this cause a crash when we try to insert
// nodes before or after this data node
if (dataNode != null) {
if (dataNode.getXFAParent() != null)
dataNodes.add(dataNode);
return;
}
// get data nodes that are peered to the children of the subformset
if (bGetChildren) {
for (Node childNode = formNode.getFirstXFAChild(); childNode != null; childNode = childNode.getNextXFASibling()) {
if (childNode instanceof Element)
getDataNodes((Element)childNode, dataNodes);
}
}
}
// Obj newClass(Element pParent, Model pModel, Node peer, int /*eClassTag*/, jfObjType) {
// return new FormInstanceManager(pParent, peer, pModel);
// }
private int getIndex() {
// get the parent node
Element parent = getXFAParent();
// find index of the template node in the form dom
int index = 0;
for (Node child = parent.getFirstXFAChild(); child != null; child = child.getNextXFASibling(), index++) {
if (child == this)
break;
}
return index;
}
private void broadcastIndexChange(int nStart, int nEnd, boolean bRange) {
FormModel formModel = (FormModel) getModel();
String sIndexChange = EnumAttr.getString(EnumAttr.ACTIVITY_INDEXCHANGE);
if (bRange) {
int nMin = Math.min(nStart, nEnd);
int nMax = Math.max(nStart, nEnd);
for (int i = nMin; i <= nMax; i++) {
formModel.eventOccurred(sIndexChange, mInstances.get(i));
}
} else {
formModel.eventOccurred(sIndexChange, mInstances.get(nStart));
if (nStart != nEnd)
formModel.eventOccurred(sIndexChange, mInstances.get(nEnd));
}
}
// only if all nodes are global or not bound
// can we restore the number of instances.
private boolean canRestoreCount(Element node, FormModel model) {
int eRetValue = model.mergeType(node, "", null);
if (eRetValue != EnumAttr.MATCH_NONE &&
eRetValue != EnumAttr.MATCH_GLOBAL)
return false;
if (node.isContainer()) {
for (Node childNode = node.getFirstXFAChild(); childNode != null; childNode = childNode.getNextXFASibling()) {
if (childNode instanceof Element)
if (!canRestoreCount((Element)childNode, model))
return false;
}
}
return true;
}
private void cleanDataNodes(Node formNode, boolean bLegacyMode) {
boolean bGetChildren = false;
DataNode dataNode = null;
if ( formNode instanceof FormSubform) {
bGetChildren = true;
FormSubform formSubform = (FormSubform)formNode;
dataNode = formSubform.getDataNode();
formSubform.cleanupListeners();
}
else if (formNode instanceof FormSubformSet) {
bGetChildren = true;
}
else if (formNode instanceof FormField) {
FormField formField = (FormField)formNode;
dataNode = formField.getDataNode();
formField.cleanupListeners();
}
else if (formNode instanceof FormExclGroup) {
bGetChildren = true;
FormExclGroup formExclGroup = (FormExclGroup)formNode;
dataNode = formExclGroup.getDataNode();
formExclGroup.cleanupListeners();
}
// add data node to list
// don't add orphaned data nodes (watson bug 1335755) this cause a crash when we try to insert
// nodes before or after this data node
// Note: Don't remove orphaned DataNodes as they will cause an exception.
if (dataNode != null && dataNode.getXFAParent() != null) {
int nPeer = 0;
int nListenerCount = 0;
Peer peer = dataNode.getPeer(nPeer);
while (peer != null) {
if (peer instanceof FormDataListener)
nListenerCount++;
nPeer++;
peer = dataNode.getPeer(nPeer);
}
// only remove the data node if there are no more data listeners
// note getDataNodes will remove the current data listener
if (nListenerCount == 0)
dataNode.remove();
// watson bug 1705701, if this is a legacy doc once we find a data node
// stop removing data nodes
if (bLegacyMode)
return;
}
// get data nodes that are peered to the children of the subformset
if (bGetChildren) {
for (Node child = formNode.getFirstXFAChild(); child != null; child = child.getNextXFASibling()) {
cleanDataNodes(child, bLegacyMode);
}
}
}
// Adobe patent application tracking # B252, entitled METHOD AND SYSTEM TO PERSIST STATE, inventors: Roberto Perelman, Chris Solc, Anatole Matveief, Jeff Young, John Brinkman
// Adobe patent application tracking # B322, entitled METHOD AND SYSTEM TO MAINTAIN THE INTEGRITY OF A CERTIFIED DOCUMENT WHILE PERSISTING STATE IN A DYNAMIC FORM, inventors: Roberto Perelman, Chris Solc, Anatole Matveief, Jeff Young, John Brinkman
public void getDeltas(Element delta, XFAList list) {
if (!isSameClass(delta))
return;
FormModel model = (FormModel)getModel();
// restore the value of the field if we are loading
if (model.isLoading()) {
FormInstanceManager deltaManager = (FormInstanceManager)delta;
ProtoableNode templateNode = mTemplateNode;
// calculate the number instances specified by the deltas
int nNewCount = 0;
int nStartIndex = deltaManager.getIndex() + 1;
Element deltaParent = deltaManager.getXFAParent();
// find the deltaChild at position nStartIndex
Node deltaChild = deltaParent.getFirstXFAChild();
for (int nChildIndex = 0; deltaChild != null; deltaChild = deltaChild.getNextXFASibling(), nChildIndex++)
if (nChildIndex == nStartIndex)
break;
// loop through the deltas and count the number of instances
for (; deltaChild != null; deltaChild = deltaChild.getNextXFASibling()) {
if (deltaChild.isSameClass(XFA.INSTANCEMANAGERTAG))
break;
if (deltaChild.isSameClass(templateNode))
nNewCount++;
}
// watson bug 1565403
// first restore the occurrence settings if we are allowed to automatically
// restore the values. If the list is not null we know merge restoreState == auto
// if we don't we can get an exception
if (list != null && delta instanceof Element) {
Element deltaOccur = ((Element)delta).getElement(XFA.OCCURTAG, true, 0, false, false);
if (deltaOccur != null) {
Element thisOccur = getElement(XFA.OCCURTAG, 0);
Int value = (Int)deltaOccur.getAttribute(XFA.MINTAG, true, false);
if (value != null)
thisOccur.setAttribute(value, XFA.MINTAG);
value = (Int)deltaOccur.getAttribute(XFA.MAXTAG, true, false);
if (value != null)
thisOccur.setAttribute(value, XFA.MAXTAG);
value = (Int)deltaOccur.getAttribute(XFA.INITIALTAG, true, false);
if (value != null)
thisOccur.setAttribute(value, XFA.INITIALTAG);
}
}
// if there are more/less instances try to add them
if (nNewCount != getCount() && canRestoreCount(templateNode, model)) {
// watson bug 1565403 don't throw an error if the new count violates min/max occurrence settings
// because an exception will cause us to corrupt the form data
setInstances(nNewCount, false);
}
}
super.getDeltas(delta, list);
}
/** @exclude from published api. */
boolean instanceManagerPermsCheck() {
// If an instance manager or any of its ancestors is/are locked, you can't addInstance, insertInstance,
// moveInstance, removeInstance, setInstances.
// Javaport: Redundant - checked by checkAncestorPerms
// if (! checkPerms())
// return false;
if (! checkAncestorPerms())
return false;
// If any of an instance manager's instances or instances' descendents is locked, you can't addInstance, insertInstance,
// moveInstance, removeInstance, setInstances.
for (int nIndex = 0; nIndex < mInstances.size(); nIndex++) {
Element instance = mInstances.get(nIndex);
if (instance != null) {
// Javaport: Redundant - checked by checkDescendentPerms
// if (! instance.checkPerms())
// return false;
if (! instance.checkDescendentPerms())
return false;
}
}
return true;
}
/**
* @exclude from published api.
*/
public ScriptTable getScriptTable() {
return FormInstanceManagerScript.getScriptTable();
}
private ProtoableNode getTemplateNode() {
return mTemplateNode;
}
public void setMatchDescendantsOnly(boolean bDescendantsOnly) {
mbMatchDescendantsOnly = bDescendantsOnly;
}
public boolean getMatchDescendantsOnly() {
return mbMatchDescendantsOnly;
}
}