All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.xfa.content.ExDataValue Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show 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.content;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

import com.adobe.xfa.Attribute;
import com.adobe.xfa.Document;
import com.adobe.xfa.Element;
import com.adobe.xfa.Int;
import com.adobe.xfa.LogMessage;
import com.adobe.xfa.Model;
import com.adobe.xfa.Node;
import com.adobe.xfa.RichTextNode;
import com.adobe.xfa.STRS;
import com.adobe.xfa.ScriptTable;
import com.adobe.xfa.StringAttr;
import com.adobe.xfa.TextNode;
import com.adobe.xfa.XFA;
import com.adobe.xfa.XMLMultiSelectNode;
import com.adobe.xfa.ut.ExFull;
import com.adobe.xfa.ut.MsgFormatPos;
import com.adobe.xfa.ut.ResId;
import com.adobe.xfa.ut.StringUtils;

/**
 * An element that describes a unit of foreign data content.
 *
 * @exclude from published api.
 */
public final class ExDataValue extends Content {

	public ExDataValue(Element parent, Node prevSibling) {
		super(parent, prevSibling, null, XFA.EXDATA, XFA.EXDATA, null,
				XFA.EXDATATAG, XFA.EXDATA);
	}
	
	/**
	 * Indicates that the children of this node should not be indexed by ID.
	 * 
	 * In C++, the children of exData are only kept in the XML DOM, so they
	 * are never indexed as part of the XFA DOM.
	 * 
	 * @exclude from published API.
	 */
	protected boolean childrenAreIndexable() {
		return false;
	}

	public boolean equals(Object object) {
		
		if (this == object)
			return true;
		
		return super.equals(object) &&
		    getValue(true, true, true).equals(((ExDataValue)object).getValue(true, true, true));
	}
	
	public int hashCode() {
		return super.hashCode() ^ getValue(true, true, true).hashCode();
	}

	public ScriptTable getScriptTable() {
		return ExDataScript.getScriptTable();
	}

	public String getStrValue() {
		return getValue(false, false, true);
	}

	public void updateIDValues(String sPrefix, List oldReferenceList) {
		// Redirect this call to the rich text node, if that's what our one-of child is.
		Node oNode = getOneOfChild(true, false);
		if (oNode instanceof RichTextNode)
			((RichTextNode) oNode).updateIDValuesImpl(sPrefix, oldReferenceList);
	}

	/**
	 * Get the text stored in this element
	 * 
	 * @return the text string
	 */
	public String getValue(
			boolean bAsFragment /* = false */,
			boolean bSuppressPreamble /* = false */,
			boolean bLegacyWhitespaceProcessing /* = false */) {
		
		String sValue = "";

		if (!getIsNull()) {
			// If there's a child on the XFA side, we need to
			// see if it's a richTextNode, textNode, or a link child

			Node node = getOneOfChild();
			if (node instanceof RichTextNode) {
				RichTextNode richTextValue = (RichTextNode) node;
				sValue = richTextValue.getValue(bAsFragment, bSuppressPreamble, bLegacyWhitespaceProcessing);
			} else if (node instanceof TextNode) {
				TextNode textValue = (TextNode) node;
				sValue = textValue.getValue();
			} else if (node instanceof XMLMultiSelectNode) {
				// multi-select choice list values
				XMLMultiSelectNode multiSelectValue = (XMLMultiSelectNode) node;
				sValue = multiSelectValue.getValue(bAsFragment,bSuppressPreamble);
			}

			// JavaPort: Dead code removed for  element.
			// The link element was only used in XFA 1.0, so it is not supported by XFA4J.
		}

		Element grandParent = null;
		if (getXFAParent() != null)
			grandParent = getXFAParent().getXFAParent();
		
		if (grandParent != null && grandParent.isSameClass(XFA.FIELDTAG) && !bAsFragment) {
			Int maxLength = (Int) getAttribute(XFA.MAXLENGTHTAG);
			int nMaxLength = maxLength.getValue();

			// If necessary, truncate the string to maxChars.
			if (0 < nMaxLength && nMaxLength < sValue.length()) {
				sValue = sValue.substring(0, maxLength.getValue());
			}
		}

		return sValue;
	}

	/**
	 * For XML node children, get the value within this element
	 * 
	 * @param values
	 *            a list of values
	 */
	public void getValues(List values) {
		
		// watson bug 1570212, when getValues is called handle the case when
		// we have null or if the values isn't a multi select node.  We run into
		// this if only one value is selected on a multi select list.
		if (!getIsNull()) {
			// watson bug 1570212
			// If there's a child on the XFA side, we need to
			// see if it's a multiselect node
			Node child = getOneOfChild(true, false);
			if (child instanceof XMLMultiSelectNode) {
				((XMLMultiSelectNode)child).getValues(values);
			}
			else {
				String sValue = getValue(false, false, true);
				values.add(sValue);
			}
		}
	}
	
	/**
	 * Text children of exData cannot be processed at parse time.
	 * This processing is deferred to postLoad processing. 
	 * 
	 * @exclude from published api.
	 */
	public boolean processTextChildrenDuringParse() {
		return false;
	}

	/**
	 * @see Element#loadXML(InputStream, boolean, ReplaceContent)
	 */
	public void loadXML(InputStream sInFile, boolean bIgnoreAggregatingTag, ReplaceContent eReplaceContent) {

		// Load the xml fragment into the XMLDOM

		// exData can only have one child - so clear any existing content before new load
		removeXmlChildren();
		
		// JavaPort: We can't call super.loadXML since this exData could be in a FormModel,
		// which explicitly doesn't allow loadXML.		
		
		Document doc = getOwnerDocument();
		Element imported = doc.loadIntoDocument(sInFile);
		Element startNode = imported.getFirstXMLChildElement();
		
		if (startNode == null)
			return;
		
		if (!bIgnoreAggregatingTag) {
			setContent(startNode, false);	
		}
		else {
			startNode = startNode.getFirstXMLChildElement();
			if (startNode != null)
				setContent(startNode, false);
		}
	}

	public void setContent(Node domNode, boolean bDefault /*  = false */) {
		mbIsNullDetermined = false;
		
		// Removing the Nil Attribute prior to populating the DOM structure,
		// so that getIsNull() does not mask the visibility of data presence in the DOM.
		// Fix for Watson bug#1242681 referring to Global binding changes in RTF.
		removeXsiNilAttribute();
		
		// Get the ExDataValueModelImpl
		Model model = getModel();

		Element element = null;
		if (domNode instanceof Element)
			element = (Element)domNode;

		// Check to see if this is an html fragment
		if (element != null && 
				(element.getNS() == STRS.XHTMLNS || 
				 element.getLocalName() == STRS.BODY || 
				 element.getLocalName() == STRS.XHTML)) {
			StringAttr prop = new StringAttr(XFA.CONTENTTYPE, STRS.TEXTHTML);
			setAttribute(prop, XFA.CONTENTTYPETAG);
			
			// Convert domNode into a RichTextNode
			RichTextNode richTextNode = new RichTextNode(null, null);
			copyDomElementOntoXfa(element, richTextNode);
			
			removeXmlChildren();			
			setOneOfChild(richTextNode);
			if (!bDefault)
				makeNonDefault(false);			

			// Do we need to bump the template version?
			// Only need to validate to bump version if bumpVersionOnRichTextLoad has been 
			// set - it is false by default and will be set true by Designer.
			if (getAppModel() != null && getAppModel().bumpVersionOnRichTextLoad()) {
				if (!validateUsage(richTextNode.getVersionRequired(), 0, true)) {
					// child first then parent
					// TODO need better error here
					MsgFormatPos reason = new MsgFormatPos(ResId.InvalidChildVersionException);
					reason.format("");
					reason.format(element.getClassAtom());
					model.addErrorList(new ExFull(reason), LogMessage.MSG_WARNING, this);
				}
			}
			
			notifyPeers(VALUE_CHANGED, "", null);
		} 
		else if (getAttribute(XFA.CONTENTTYPETAG).toString().equals(STRS.TEXTXML)) {
			// multi-select choice list values
			
			Node newContent = null;
			
			if (element != null) {
				// Convert domNode into an XMLMultiSelectNode
				XMLMultiSelectNode xmlMultiSelectNode = new XMLMultiSelectNode(null, null);
				copyDomElementOntoXfa(element, xmlMultiSelectNode);
				
				newContent = xmlMultiSelectNode;
			}
			else if (domNode instanceof TextNode) {
				newContent = domNode;
			}
			
			removeXmlChildren();
			setOneOfChild(newContent);
			if (!bDefault)
				makeNonDefault(false);
			
			notifyPeers(VALUE_CHANGED, "", null);
		} else {
			// If the contentType is text/html, then clear it. So the content
			// won't be treated as richtext
			if (getAttribute(XFA.CONTENTTYPETAG).toString().equals(STRS.TEXTHTML)) {
				StringAttr prop = new StringAttr(XFA.CONTENTTYPE, "");
				setAttribute(prop, XFA.CONTENTTYPETAG);
			}

			// text nodes
			if (domNode instanceof TextNode) {
				removeXmlChildren();
				setOneOfChild(domNode);
				if (!bDefault)
					makeNonDefault(false);
				notifyPeers(VALUE_CHANGED, "", null);
			} 
			else {
				
				// JavaPort: In the C++, loadXML will have appended domNode to the
				// peer, even if we log an error here.
				appendChild(domNode, false);
				
				// Note that the unknown content is left in place as generic nodes
				// (of class XFA.INVALID_ELEMENT). This replicates the C++ behaviour
				// where there would be no child element of exData in the XFA DOM, but there
				// would be a child in the XML DOM.
				
				// Give an error, unknown content
				// Log a warning, invalid content type.
				// %1 is unable to support the specified content. Hint(%2)
				MsgFormatPos format = new MsgFormatPos(ResId.UnsupportedContentWarning);
				format.format(getClassAtom());
				format.format(domNode.getName());
				ExFull err = new ExFull(format);
				// TODO JavaPort: start using log severities
				model.addErrorList(err, LogMessage.MSG_WARNING /* 0 */, this);
			}
		}
	}
	
	private void removeXmlChildren() {
		Node child = getFirstXMLChild();
		while (child != null) {
			removeChild(child);
			child = getFirstXMLChild();
		}
	}
	
	/**
	 * Copies an XML DOM element onto an existing XFA element.
	 * This is used in the case where in C++ a RichTextNode or XMLMultiSelectNode would
	 * be peered to an XML DOM node. In XFA4J, the the XFA node represents both the
	 * XFA and XML DOM node. The children of domNode need to be moved to the XFA node
	 * since there is no XML DOM. This assumes that domNode has already been imported
	 * so it doesn't need to be copied again.
	 *  
	 * @param domNode the XML DOM node that is being peered to xfaNode.
	 * @param xfaNode the RichTextNode or XMLMultiSelectNode that is being peered.
	 */
	private void copyDomElementOntoXfa(Element domNode, Element xfaNode) {
		// JavaPort: in C++, the RichTextNode would be peered to domNode, but
		// here it replaces it.
		
		xfaNode.setModel(getModel());
		xfaNode.setDocument(getOwnerDocument());
		
		xfaNode.setDOMProperties(domNode.getNS(), domNode.getLocalName(), domNode.getXMLName(), null);
		
		// Copy attributes
		for (int i = 0; i < domNode.getNumAttrs(); i++) {
			Attribute a = domNode.getAttr(i);
			xfaNode.setAttribute(a.getNS(), a.getQName(), a.getLocalName(), a.getAttrValue(), false);
		}
		
		// Node children
		// The child nodes have already been imported, and can simply be copied.
		Node nextSibling;
		for (Node child = domNode.getFirstXMLChild(); child != null; child = nextSibling) {
			nextSibling = child.getNextXMLSibling();
			xfaNode.appendChild(child);
		}
	}

	/**
	 * For XML node children, set the value within this element
	 * 
	 * @param values
	 *            a list of values to set. (Strings)
	 */
	public void setValue(List values) {
		if (getAttribute(XFA.CONTENTTYPETAG).toString().equals(STRS.TEXTXML)) {
			if (getFirstXFAChild() != null) {
				// multi-select choice list values
				Node child = getFirstXFAChild();
				if (child instanceof XMLMultiSelectNode) {
					((XMLMultiSelectNode)child).setValues(values);
					return; // we are done;
				}
				
				// invalid content, remove all the children
				while (getFirstXMLChild() != null) {
					getFirstXMLChild().remove();
				}	
			}
			
			// create the XMLMULTISELECTNODETAG
			Model model = getModel();
			XMLMultiSelectNode newNode = (XMLMultiSelectNode)model.getSchema().getInstance(
					XFA.XMLMULTISELECTNODETAG, model, this, null, false);

			// watson 1776125 we must create our own dom node since #xml is not valid xml. 
			// To find the name go up and get the fields/draw name if that doesn't exist default to "items".
			Element parent = getXFAParent();
			while (parent != null && !parent.isContainer())
				parent = parent.getXFAParent();

			String aName = XFA.ITEMS;
			if (parent != null)
				aName = parent.getName();
			
			// create the dom node with the same name as the field/draw.
			newNode.setLocalName(aName);
		
			// save the values
			newNode.setValues(values);
		}
		else {
			String sValue = (String) values.get(0);
			setValue(sValue);
		}
	}

	/**
	 * Set the text value within this element
	 * 
	 * @param sText
	 *            a string to set/replace the text stored in this element
	 */
	public void setValue(String sText) {
		String sContent = getAttribute(XFA.CONTENTTYPETAG).toString();

		if (sContent.equals(STRS.TEXTXML)) {
			
			// watson bug 1856188 we may not have a multi select node,
			// so call setValue() with a string list since this function 
			// can create a new multi select node.
			
			// parse input string, each line is a new item value
			List selectionList = new ArrayList();
			if (!StringUtils.isEmpty(sText)) {
				StringTokenizer tokenizer = new StringTokenizer(sText, "\n");
				
				while (tokenizer.hasMoreTokens()) {
					selectionList.add(tokenizer.nextToken());
				}
			}
			
			setValue(selectionList);

		} else {
			// if we're assigning plain text to a rich text field, then change
			// the content type.
			if (StringUtils.isEmpty(sContent) || sContent.equals(STRS.TEXTHTML))
				setAttribute(new StringAttr(XFA.CONTENTTYPE, STRS.TEXTPLAIN), XFA.CONTENTTYPETAG);
			setStrValue(sText, true, false);
		}
	}

	/**
	 * Cast this element to a String. This allows this object to be used in
	 * contexts where a string is expected without needing to explicitly call
	 * getValue().
	 * 
	 * @return the text value.
	 */
	public String toString() {
		return getValue(false, false, true);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy