![JAR search and dependency download from the Maven repository](/logo.png)
com.adobe.xfa.service.storage.XMLStorage 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
/*
* 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.service.storage;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.List;
import com.adobe.xfa.AppModel;
import com.adobe.xfa.DOMSaveOptions;
import com.adobe.xfa.Document;
import com.adobe.xfa.Element;
import com.adobe.xfa.LogMessage;
import com.adobe.xfa.Model;
import com.adobe.xfa.ModelFactory;
import com.adobe.xfa.Node;
import com.adobe.xfa.NodeList;
import com.adobe.xfa.Option;
import com.adobe.xfa.ProcessingInstruction;
import com.adobe.xfa.STRS;
import com.adobe.xfa.XFA;
import com.adobe.xfa.data.DataModel;
import com.adobe.xfa.ut.Assertions;
import com.adobe.xfa.ut.ExFull;
import com.adobe.xfa.ut.FindBugsSuppress;
import com.adobe.xfa.ut.MsgFormat;
import com.adobe.xfa.ut.ResId;
import com.adobe.xfa.ut.StringUtils;
import com.adobe.xfa.ut.Version;
/**
* The XML storage service. Many of these methods take an optional
* parameter called either loadOptions or saveOptions. These specify extra
* options, separated by blanks. These appear as they would in an input file,
* e.g. name="value" name="value"...
*
* Options
*
*
* - "processXSL" (boolean, '1' or '0')
*
- '1' if XSL processing instructions encountered
* in the file should be applied, '0' if not.
*
- "XSL" (string)
*
- The filename of an XSL script to apply to the input stream.
*
- "incrementalLoad" (boolean, '1' or '0')
*
- '1' if incremental loading (or lazy loading)
* should be used to load this file, '0' if the entire file should be loaded
* into memory at once.
*
- "Model" (string)
*
- Name of model to load, if stream contains multiple models.
*
- "setPI" (string)
*
- A processing instruction to set. The format is setPI="name value". The
* name of the processing instruction is the set of characters before the first whitespace.
* The value is everything after the first whitespace.
*
- "Option" (string)
*
- An XFA option to set. The value should be of the form name="value" or
* name='value'.
*
- "format" (string, "raw" or "simple" or "pretty")
*
- Set to "raw" to save with no whitespace
* or carriage returns; "simple" to add carriage returns but no whitespace; "pretty"
* to add carriage returns and whitespace to show the hierarchy of the XML file.
*
- "encoding" (string)
*
- The character set to use when writing the XML file.
*
- "indentLevel" (integer)
*
- the number of spaces to indent each nested level,
* only applied to pretty output
*
- "attributeQuoteChar" (char, " or ' )
*
- if ' attributes are enclosed in single quotes, if "
* attributes are enclosed by double quotes (default)
*
- "entityChars" (string)
*
- A list of individual characters to be represent with entity references.
* The default list will vary according to whether we're writing attributes
* or text values. This list will be added to the default list.
* e.g. "\x0A\x09" for linefeed and cr.
*
- "minEntityCharRange" (int)
*
- Any character value greater than or equal to this int are encoded with their entity references.
*
- "maxEntityCharRange" (int)
*
- Any character value less than or equal to this int are encoded with their entity references.
*
*
*
* @author Darren Burns
* @exclude from published api -- Mike Tardif, May 2006.
*/
public final class XMLStorage extends StorageService {
static private final String gsAttrQChar = "attributeQuoteChar";
static private final String gsCharArg = "'|\"";
static private final String gsEntityChars = "entityChars";
static private final String gsFormatArg = "raw|simple|pretty";
static private final String gsIncrementalLoadArg = "none|forwardOnly";
static private final String gsIndentLevel = "indentLevel";
static private final String gsMaxEntityCharRange = "maxEntityCharRange";
static private final String gsMinEntityCharRange = "minEntityCharRange";
static private final String gsModel = "Model";
static private final String gsPercentD = "%d";
static private final String gsProcessXSL = "processXSL";
static private final String gssetPI = "setPI";
static private final String gsXFAOption = "XFAOption";
static private final String gsXSL = "XSL";
static private final String gsXSLFile = "XSLFile";
/**
* This routine currently is called only with piName being "xfa". Its
* purpose is to find all the processing instructions with the name in
* piName, and return the concatenation of the values of all those PIs. It
* also compacts the values of those PIs into the first occurrence.
* Normally, there will only be one occurrence, in which case this routine
* will not modify the data.
*/
static String compactPIs(String piName, Document doc) {
assert(piName != null);
ProcessingInstruction firstPI = null;
// set to the first PI found whose name is piName
Node oChild = doc.getFirstXMLChild();
while (oChild != null) {
Node oNext = oChild.getNextXMLSibling();
if ((oChild instanceof ProcessingInstruction)
&& oChild.getName().equals(piName)) {
if (firstPI == null) {
firstPI = (ProcessingInstruction) oChild;
}
else {
String sData = firstPI.getData() + ' ' + ((ProcessingInstruction) oChild).getData();
firstPI.setData(sData);
// delete this node
doc.removeChild(oNext);
}
}
if (oChild instanceof Element)
break;
oChild = oNext;
}
if (firstPI == null)
return "";
return firstPI.getData();
}
/**
* setGenerator is a static routine which specifies the generator tag to be
* written out when a model is saved to an XML file. It should be called
* once by any application that will be saving files.
If this routine
* is not invoked, the default value will be XFA2_0.
*
* @param newGeneratorTag
* the new value for the generator tag.
*/
public void setGenerator(String newGeneratorTag) {
//msGeneratorTag = newGeneratorTag;
}
static void updateProcessingInstruction(Document doc,
String piName, String piValue) {
assert(piName != null);
if (Assertions.isEnabled) assert piName == piName.intern();
// Replace the value of any existing generator tag. We can't just
// delete the tag that contains "generator" because there may be more
// than one option in the processing instruction, e.g.
//
// If no value is found, then a new processing instruction is created.
boolean bValueReplaced = false;
Node oChild = doc.getFirstXMLChild();
while (oChild != null) {
if (oChild instanceof ProcessingInstruction &&
oChild.getName().equals(piName)) {
((ProcessingInstruction) oChild).setData(piValue);
bValueReplaced = true;
break; // replace only first
}
if (oChild instanceof Element)
break;
oChild = oChild.getNextXMLSibling();
}
if ( ! bValueReplaced) {
Node generatorNode
= new ProcessingInstruction(null, piName, piValue);
doc.insertChild(generatorNode, doc.getFirstXMLChild(), false);
}
}
// in name="value" name2="value" string, replace or append name/value pair
static String updateValue(String nodeValue,
String optionName, String optionValue) {
StringBuilder sNodeValue = new StringBuilder(nodeValue);
boolean bValueReplaced = false;
char cDoubleQuote = '"';
char cSingleQuote = '\'';
String namePrefix = optionName + "=";
int nFoundAt = nodeValue.indexOf(namePrefix);
if (nFoundAt >= 0) {
int nValueStart = nFoundAt + namePrefix.length() + 1;
char cQuote = ' ';
if (nodeValue.charAt(nValueStart - 1) == cDoubleQuote)
cQuote = cDoubleQuote;
else if (nodeValue.charAt(nValueStart - 1) == cSingleQuote)
cQuote = cSingleQuote;
// else case is not too likely, but it's not what we're looking for
if (cQuote != ' ') {
int nValueEnd = nodeValue.indexOf(cQuote, nValueStart);
if (nValueEnd < 0)
nValueEnd = nodeValue.length() - 1; // shouldn't happen
nValueEnd--;
sNodeValue.replace(nValueStart, nValueEnd + 1, optionValue);
bValueReplaced = true;
}
}
if ( ! bValueReplaced) {
if (sNodeValue.length() > 0)
sNodeValue.append(' ');
sNodeValue.append(namePrefix);
sNodeValue.append(cDoubleQuote);
sNodeValue.append(optionValue);
sNodeValue.append(cDoubleQuote);
}
return sNodeValue.toString();
}
//private String msGeneratorTag = "";
// inherited from class XFAStorageService
private final Option mGeneratorOption; // name of generator
private final Option mAPIVersionOption; // API version used to create the file
// Options used when loading
private final Option mProcessXSLOption; // (boolean) true if should process XSL on load
private final Option mXSLOption; // (string) name of XSL file to apply on load
private final Option mIncrementalOption; // (boolean) true if should do incremental loading (lazy loading)
private final Option mModelOption; // (string) name of model to load
// Options used when saving
private final Option mSetPIOption; // (string) extra processing instruction
private final Option mXFAPIOption; // (string) extra XFA processing instruction
private final Option mFormatOption; // "raw|simple|pretty"
private final Option mEncodingOption; // (string) code page, eg. "UTF-16"
private final Option mIndentLevelOption; // (int) number of spaces to indent for each nested level
private final Option mAttributeQuoteCharOption; // "|'
private final Option mEntityCharsOption; // (string) characters to encode with references, eg. "'<&"
private final Option mMaxEntityCharRangeOption; // (int) chars < this are encoded with references, eg. "65535"
private final Option mMinEntityCharRangeOption; // (int) chars > this are encoded with references, eg. "128"
private final Option mXSLFileOption; // (string) the input xsl file to apply to data
private final File moFileName;
private final URL mUrl;
public XMLStorage() {
this(null, null);
}
/**
* Constructs an XMLStorage object, specifying a data file name that will
* be used for loading or saving data.
*
* @param xmlFile
* the name of the file containing the source XML.
*/
public XMLStorage(File xmlFile) {
this(xmlFile, null);
}
/**
* Constructs an XMLStorage object, specifying a data file URL that will
* be used for loading or saving data.
*
* @param url
* the URL containing the source XML.
*/
public XMLStorage(URL url) {
this(null, url);
}
private XMLStorage(File xmlFile, URL url) {
moFileName = xmlFile;
mUrl = url;
mGeneratorOption = new Option(STRS.GENERATOR, STRS.PERCENTS);
mAPIVersionOption = new Option(STRS.APIVERSION, STRS.PERCENTS);
mProcessXSLOption = new Option(gsProcessXSL, STRS.PERCENTB);
mXSLOption = new Option(gsXSL, STRS.PERCENTS);
mModelOption = new Option(gsModel, STRS.PERCENTS);
mIncrementalOption = new Option(XFA.INCREMENTALLOAD, gsIncrementalLoadArg);
mSetPIOption = new Option(gssetPI, STRS.PERCENTS);
mXFAPIOption = new Option(gsXFAOption, STRS.PERCENTS);
mFormatOption = new Option(XFA.FORMAT, gsFormatArg);
mEncodingOption = new Option(XFA.ENCODING, STRS.PERCENTS);
mIndentLevelOption = new Option(gsIndentLevel, gsPercentD);
mAttributeQuoteCharOption = new Option(gsAttrQChar, gsCharArg);
mEntityCharsOption = new Option(gsEntityChars, STRS.PERCENTS);
mMinEntityCharRangeOption = new Option(gsMinEntityCharRange, gsPercentD);
mMaxEntityCharRangeOption = new Option(gsMaxEntityCharRange, gsPercentD);
mXSLFileOption = new Option(gsXSLFile, STRS.PERCENTS);
}
/**
* Returns the API version tag from the XML file. This allows an application
* to determine which version of the XFA API was used to write the XML file.
* If not specified in the file, this will return an empty string.
*
* @return The value of the APIVersion tag, or an empty string if no
* APIVersion tag was encountered.
*/
public String getAPIVersion() {
if (! mAPIVersionOption.isSet())
return "";
return mAPIVersionOption.getString();
}
/**
* Returns the "generator" tag from the XML file. This allows an application
* to determine which application wrote the XML file. If not specified in
* the file, this will return an empty string.
*
* @return The value of the generator tag, or an empty string if no
* generator tag was encountered.
*/
public String getGenerator() {
if (! mGeneratorOption.isSet())
return "";
return mGeneratorOption.getString();
}
/**
* Loads the contents of an InputStream into an AppModel, creating an
* Node hierarchy.
*
* This overload does not provide the source of the document, so any
* capabilities that may rely on this (e.g., resolving external fragments
* that use relative references) will not work. If the source of the input stream
* is known, the {@link XMLStorage#loadModel(AppModel, InputStream, String, String, String)}
* overload should be used instead.
*
* @param model
* the AppModel to be populated.
* @param inputStream
* an open file to be read in.
* @param loadOptions
* see the comment in the class description.
* @param saveXSLFile
* specifies the name of a file to save intermediate
* XSL output. This is intended as an aid to debugging only.
*/
public Node loadModel(
AppModel model,
InputStream inputStream,
String loadOptions /* = "" */,
String saveXSLFile /* = null */) {
return loadModel(model, inputStream, null, null, loadOptions, saveXSLFile);
}
/**
* Loads the contents of an InputStream into an AppModel, creating an
* Node hierarchy.
*
* @param model
* the AppModel to be populated.
* @param inputStream
* an open file to be read in.
* @param source
* the absolute file or URL name that inputStream was loaded from.
* @param loadOptions
* see the comment in the class description.
* @param saveXSLFile
* specifies the name of a file to save intermediate
* XSL output. This is intended as an aid to debugging only.
*/
public Node loadModel(
AppModel model,
InputStream inputStream,
String source,
String loadOptions /* = "" */,
String saveXSLFile /* = null */) {
return loadModel(model, inputStream, source, null, loadOptions, saveXSLFile);
}
/**
* Loads the contents of an InputStream into an AppModel, creating an
* Node hierarchy.
*
* @param model
* the XFAModel to be populated.
* @param file
* a file to be read in.
* @param loadOptions
* see the comment in the class description.
* @param saveXSLFile
* specifies the name of a file to save intermediate
* XSL output. This is intended as an aid to debugging only.
*/
public Node loadModel(
AppModel model,
File file,
String loadOptions /* = "" */,
String saveXSLFile /* = null */) {
return loadModel(model, null, null, file, loadOptions, saveXSLFile);
}
/**
*/
private Node loadModel(
AppModel appModel,
InputStream is,
String source,
File file,
String loadOptions /* = "" */,
String saveXSLFile /* = null */) {
setOptions(loadOptions, appModel);
// clear the errorlist so we don't get errors from previous loads
appModel.getErrorList();
if (!StringUtils.isEmpty(saveXSLFile)) {
// Before an attempt is made to parse the XSL file, create a dummy file,
// so that we don't confuse people by leaving older versions of the file around.
// This file will get overwritten if the XSL is successfully parsed.
FileOutputStream oStreamFile = null;
try {
oStreamFile = new FileOutputStream(saveXSLFile);
String sContents = " ";
oStreamFile.write(sContents.getBytes(Document.Encoding));
}
catch (IOException e) {
throw new ExFull(e);
}
finally {
if (oStreamFile != null) {
try { oStreamFile.close(); }
catch (IOException ignore) { }
}
}
}
// boolean bProcessXSL = true;
// if (mProcessXSLOption.isSet())
// bProcessXSL = mProcessXSLOption.getBool();
Document doc = appModel.getDocument().isDefaultDocument() ?
appModel.getDocument() : Document.createDocument(appModel);
boolean bLazyLoading = false;
if (mIncrementalOption.isSet()) {
if (mIncrementalOption.getMatchingIndex() == 0) // "none"
bLazyLoading = false;
else if (mIncrementalOption.getMatchingIndex() == 1) // "forwardOnly"
bLazyLoading = true;
}
if ((!bLazyLoading) && ((mXSLOption.isSet()) || (mXSLFileOption.isSet()))) {
throw new ExFull(ResId.UNSUPPORTED_OPERATION, "XMLStorage#loadModel - XSL");
// Javaport: TODO
// // An explicit XSL script is specified. We must manually transform the
// // stream using a jfXSLTranslator.
//
// // convert filename into a jfStreamFile
// jfStreamFile XSLfile;
//
// jfMemoryStreamFile oMemXSLfile;
// jfMemoryStreamFile memstream;
//
// if (mXSLOption.isSet()) {
// // we have the name of the xsl file
// XSLfile.Open(mXSLOption.getString(), jfFileMode());
// XSLTranslator translator = new XSLTranslator(XSLfile);
// translator.process(const_cast(file), memstream);
// // translate specified stream into a memory stream
// }
// else {
// // we have the xsl file contents in a string
// String sXSL = mXSLFileOption.getString();
// int nLength = sXSL.length();
// char*s = (char*)sXSL;
// oMemXSLfile.Write((void *)s, nLength);
// oMemXSLfile.Position(jfFilePosition()); // reset memory stream
//
// XSLTranslator translator = new XSLTranslator(oMemXSLfile);
// translator.process(const_cast(file), memstream);
// // translate specified stream into a memory stream
// }
//
// memstream.Position(jfFilePosition()); // reset memory stream
//
// boolean bException = false;
// ExFull oThrow = new ExFull(DOM_TRANSFORMED_FILE_ERR);
// try {
// doc = jfDomDocument.load(memstream, bProcessXSL);
// } catch (jfExFull & oEx) // {
// bException = true;
// oThrow.Insert(oEx,true);
// }
//
// if (!oSaveXSLFile.IsEmpty ()) {
// jfDomDocument debugDoc = jfDomDocument.createDocument();
// String sDebugUri("http://www.xfa.org/schema/xfa-xslt-debug/1.0/");
// jfDomElement xlstDebug = debugDoc.createElementNS(sDebugUri, "xfa:xsltDebug");
// debugDoc.appendChild(xlstDebug);
// jfDomElement xlstResult = debugDoc.createElementNS(sDebugUri, "xfa:xsltResult");
// xlstDebug.appendChild(xlstResult);
//
// if (bException) {
// // parsing failed; insert the contents of the memstream into a
// // CDATA for the debug file
// String sData;
// memstream.Position(jfFilePosition()); // reset memory stream
// memstream.ReadFile(sData);
// jfDomCDATASection cdata = debugDoc.createCDATASection(sData);
// xlstResult.appendChild(cdata);
// }
// else {
// // no exception -- insert contents of doc into debugDoc
// jfDomNodeList children = doc.getChildNodes();
// int numChildren = children.getLength();
// for (int i = 0; i < numChildren; i++) {
// jfDomNode oNode = debugDoc.importNode(children.item(i), true);
// xlstResult.appendChild(oNode);
// }
// }
// debugDoc.saveAs(oSaveXSLFile);
// }
//
// if (bException)
// throw oThrow;
}
else if (bLazyLoading) {
throw new ExFull(ResId.UNSUPPORTED_OPERATION);
// Javaport: TODO
// doc = jfDomDocument.createDocument(const_cast(file));
// if (mXSLOption.isSet()) {
// // store the XSL script name in the dom document so that it can
// // be applied to each record
// jfDocumentImpl *poDomDocumentImpl = (jfDocumentImpl*) /&doc.getObj();
// poDomDocumentImpl.XSLScriptName(mXSLOption.getString(), oSaveXSLFile);
// }
// else if (mXSLFileOption.isSet()) {
// // store the XSL script name in the dom document so that it can
// // be applied to each record
// jfDocumentImpl *poDomDocumentImpl = (jfDocumentImpl*) &doc.getObj();
// poDomDocumentImpl.XSLScriptFile(mXSLFileOption.getString(), oSaveXSLFile);
// }
// doc.loadToNextElement();
}
else {
try {
if (file != null)
doc.load(file);
else
doc.load(is, source, null, false /*, bProcessXSL */);
} catch (ExFull e) {
// alternatively, severity could be MSG_FATAL_ERROR.
appModel.addErrorList(e, LogMessage.MSG_WARNING, appModel);
}
}
Element startNode = null;
// Start with the first element node.
// skip comments, processing instructions etc.
for (Node child = doc.getFirstXMLChild(); child != null; child = child.getNextXMLSibling()) {
if (child instanceof ProcessingInstruction &&
child.getName() == XFA.XFA) {
setOptions(((ProcessingInstruction) child).getData(), appModel);
}
if (child instanceof Element) {
startNode = (Element) child;
break;
}
}
if (startNode != null) {
// If a single model was specified, only load that XFA model (i.e. remove the other models).
// JavaPort: All model loading has been done by this point, so we simply remove other
// models if the model option is set.
if (mModelOption.isSet()) {
String sModelName = mModelOption.getString();
Node nextSibling;
for (Node domPacket = startNode.getFirstXMLChild(); domPacket != null; domPacket = nextSibling) {
String aPacketName = domPacket.getName();
nextSibling = domPacket.getNextXMLSibling();
// Check to see if it's an XFA Packet.
List factories = appModel.factories();
for (int i = 0; i < factories.size(); i++) {
ModelFactory factory = factories.get(i);
if (factory.isRootName(aPacketName)) {
// Remove any xfa models not specified.
if (!factory.isRootName(sModelName))
domPacket.getXMLParent().removeChild(domPacket);
break;
}
}
}
}
}
// JavaPort: This logic from C++ can't be done here since all model loading
// has been done by this point in the Java implementation. However, this logic
// needs to be implemented elsewhere.
//
// Generator generator = new Generator(getGenerator(), getAPIVersion());
// if (pModelImpl.getDomPeer().isNull()) {
// pModelImpl.setDomPeer(startNode);
// // load starting at dom peer
// pModelImpl.loadChildren(startNode, generator);
// }
// else {
// // merge into existing model
// pModelImpl.add(startNode, generator);
// }
doc.isDefaultDocument(false);
appModel.cleanDirtyFlags();
return appModel;
}
/**
* Loads the file/datasource specified in the constructor into a model and
* creates an Node hierarchy for it.
*
* @param model
* the AppModel to be populated with the data from the
* datasource.
* @param loadOptions
* see the comment in the class description.
* @param saveXSLFile
* specifies the name of a file to save intermediate
* XSL output. This is intended as an aid to debugging only.
* @return The root node of the hierarchy
*/
@FindBugsSuppress(code="DE")
public Node loadModel(
AppModel model,
String loadOptions /* = "" */,
String saveXSLFile /* = null */) {
Node node;
if (moFileName != null) {
node = loadModel(model, null, null, moFileName, loadOptions, saveXSLFile);
}
else if (mUrl != null) {
InputStream is = null;
try {
is = mUrl.openStream();
node = loadModel(model, is, mUrl.toString(), null, loadOptions, saveXSLFile);
}
catch (IOException ex) {
throw new ExFull(ex);
}
finally {
if (is != null) {
try { is.close(); }
catch (IOException ignored) { }
}
}
}
else {
// If the caller didn't specify either a File or an URL, then
// this object isn't in a valid state to call this method.
throw new IllegalStateException();
}
return node;
}
/**
* Loads the contents of a XDP stream into an AppModel, creating an
* Node hierarchy.
*
* @param appModel
* the AppModel to be populated.
* @param file
* an open file to be read in. We assume the file is positioned
* appropriately.
* @param handler
* Handler to do any special processing of packets
* before XFA DOM is created.
* @param handlerData
* Handler data.
*/
public boolean loadXDP(AppModel appModel, InputStream file,
PacketHandler handler, Object handlerData,
boolean bProcessXFAOnly /* = false */) {
Document doc = appModel.getDocument();
// JavaPort: packet filtering can't be done post document load.
// Hence register the packet handler with the model instead.
appModel.setPacketHandler(handler, handlerData);
doc.load(file, null, false);
return loadXDP(appModel, doc, null, null, bProcessXFAOnly);
}
/*
* Takes a document directly.
* @exclude from published api.
*/
public boolean loadXDP(AppModel oAppModel, Document oDoc,
PacketHandler handler, Object handlerData,
boolean bProcessXFAOnly /* = false */) {
Node startNode = null;
Node oChild = oDoc.getFirstXMLChild();
while (oChild != null) {
if (oChild instanceof ProcessingInstruction
&& ((ProcessingInstruction)oChild).getName() == XFA.XFA) {
setOptions(((ProcessingInstruction) oChild).getData(), oAppModel);
}
else if (oChild instanceof Element) {
startNode = oChild;
break;
}
oChild = oChild.getNextXMLSibling();
}
// JavaPort: changed from C++. In XFA4J, isXFANode() has broken out the uri
// and qname as separate arguments. That appears to be for the SAXHandler.
// Note that qname is never used in isXFANode(). Anyway, if the startNode
// is an element, then get its uri and qname.
String uri = "";
String qname = "";
if (startNode instanceof Element) {
uri = ((Element) startNode).getNS();
qname = ((Element) startNode).getXMLName();
}
//
// bad start node return null root
//
if (startNode == null
|| (bProcessXFAOnly
&& ! oAppModel.isXFANode(uri, startNode.getName(), qname)))
return false;
if (handler != null) {
// Process xdp packets.
if (oAppModel.isXFANode(uri, startNode.getName(), qname)) {
Node oDomPacket = startNode.getFirstXMLChild();
// process packets.
// for each element check if it matches the packet
// skip comments, processing instructions etc.
while (oDomPacket != null) {
Node oNextSibling = oDomPacket.getNextXMLSibling();
if (oDomPacket instanceof Element) {
Element oPacket = (Element) oDomPacket;
handler.filterPackets(oPacket, handlerData);
}
oDomPacket = oNextSibling;
}
}
else {
// Process a single xml node.
handler.filterPackets(startNode, handlerData);
}
}
// JavaPort: this is not supportable in Java, and is a change in behaviour.
// In C++, this builds up the XFA DOM from an XML document. In Java, the
// XFA DOM is already present in the document.
// Generator generator = new Generator(getGenerator(), getAPIVersion());
// if (oAppModel == null) {
// // load starting at dom peer
// oAppModel.loadChildren(startNode, generator);
// }
// else {
// // merge into existing model
// oAppModel.add(startNode, generator);
// }
return true;
}
/**
* Specifies whether or not to automatically process inline XSL statements
* when loading the XML file.
*
* @param process
* TRUE if XSL should be automatically processed, FALSE if not.
* This flag is simply stored and passed to
* Document.load
when one of the load routines is
* called.
*/
public void processXSL(boolean process) {
// TODO Auto-generated method stub
throw new ExFull(ResId.UNSUPPORTED_OPERATION, "XMLStorage#processXSL");
}
/**
* Save a group of nodes under an aggregating tag.
*
* @param sRoot
* The name to use for the aggregating tag. If sRoot is empty,
* then the standard xdp:xdp with appropriate namespace will be
* used.
* @param outStream
* The output stream to write to.
* @param oNodes
* The list of XFA Nodes to write
* @param saveOptions
* (optional) see the comment in the class description.
*/
public void saveAggregate(String sRoot, OutputStream outStream,
NodeList oNodes, String saveOptions) {
setOptions(saveOptions, null);
//
// Update our internal generator tag
//
mGeneratorOption.setValue(getGenerator(), false);
DOMSaveOptions oOptions = new DOMSaveOptions();
try {
if (oNodes.length() > 0 && ! oOptions.getExcludePreamble()) {
// Update the options in the context of our App model document
Model oModel = ((Node)oNodes.item(0)).getModel();
AppModel oAppModel = null;
if (oModel != null)
oAppModel = oModel.getAppModel();
if (oAppModel != null) {
Document doc = oAppModel.getDocument();
updateOptions(doc, oOptions);
// Write out xml processing instruction
doc.savePreamble(outStream, oOptions);
// Write out the xfa processing instruction
String xfaValue = compactPIs(XFA.XFA, doc);
if (!StringUtils.isEmpty(xfaValue)) {
outStream.write(Document.MarkupPIStart);
outStream.write(XFA.XFA.getBytes(Document.Encoding));
outStream.write(Document.MarkupSpace);
outStream.write(xfaValue.getBytes(Document.Encoding));
outStream.write(Document.MarkupPIEnd);
outStream.write(Document.MarkupReturn);
}
}
}
oOptions.setExcludePreamble(true);
// TBD: respect the save format in saveOptions properly.
if (StringUtils.isEmpty(sRoot)) {
//
// create the standard xdp:xdp root node
//
outStream.write(" 0) {
//
// check for XDP attributes
//
Model oModel = ((Node)oNodes.item(0)).getModel();
AppModel oAppModel = null;
if (oModel != null)
oAppModel = oModel.getAppModel();
if (oAppModel != null) {
String sAttrVal = oAppModel.getAttribute(XFA.TIMESTAMPTAG).toString();
if (sAttrVal.length() > 0) {
outStream.write(Document.MarkupSpace);
outStream.write(XFA.TIMESTAMP.getBytes(Document.Encoding));
outStream.write(Document.MarkupAttrMiddle);
outStream.write(sAttrVal.getBytes(Document.Encoding));
outStream.write(Document.MarkupDQuoteString);
}
sAttrVal = oAppModel.getAttribute(XFA.UUIDTAG).toString();
if (sAttrVal.length() > 0) {
outStream.write(Document.MarkupSpace);
outStream.write(XFA.UUID.getBytes(Document.Encoding));
outStream.write(Document.MarkupAttrMiddle);
outStream.write(sAttrVal.getBytes(Document.Encoding));
outStream.write(Document.MarkupDQuoteString);
}
}
}
outStream.write(Document.MarkupEndTag);
outStream.write(Document.MarkupReturn);
}
else {
outStream.write(Document.MarkupStartTag);
outStream.write(sRoot.getBytes(Document.Encoding));
outStream.write(Document.MarkupEndTag);
outStream.write(Document.MarkupReturn);
}
for (int i = 0; i < oNodes.length(); i++) {
Node oNode = (Node) oNodes.item(i);
Document oDoc = oNode.getOwnerDocument();
if (oNode instanceof Element)
((Element)oNode).preSave(false);
// if the node doesn't have a previous sibling,
// fake the output in thinking it does. This is
// to get a newline.
if (oNode.getPreviousXMLSibling() == null)
outStream.write(Document.MarkupReturn);
if (oDoc != null)
oDoc.saveAs(outStream, oNode, oOptions);
if (oNode instanceof Model)
((Model)oNode).postSave();
}
if (StringUtils.isEmpty(sRoot)) {
outStream.write(Document.MarkupCloseTag);
outStream.write(STRS.XDPNODE.getBytes(Document.Encoding));
outStream.write(Document.MarkupEndTag);
outStream.write(Document.MarkupReturn);
}
else {
outStream.write(Document.MarkupCloseTag);
outStream.write(sRoot.getBytes(Document.Encoding));
outStream.write(Document.MarkupEndTag);
outStream.write(Document.MarkupReturn);
}
}
catch (IOException e) {
throw new ExFull(e);
}
}
/**
* Saves a model into a file/datasource, starting at the root.
*
* @param model
* the XFAModel to be saved into the datasource.
* @param saveOptions
* see the comment in the class description.
*/
public void saveModel(Model model, String saveOptions /* = "" */) {
try {
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(moFileName));
saveModelAs(model, outputStream, saveOptions);
outputStream.close();
}
catch (IOException ex) {
throw new ExFull (ex);
}
}
/**
* Saves a model to an open stream file.
*
* @param model
* the XFAModel to be saved.
* @param file
* the open streamfile to write to.
* @param saveOptions
* see the comment in the class description.
*/
public void saveModelAs(Model model, OutputStream file, String saveOptions /* = "" */) {
setOptions(saveOptions, model);
// Save the document that model was loaded with.
Document doc = model.getDocument();
DOMSaveOptions options = new DOMSaveOptions();
updateOptions(doc, options);
model.preSave(false);
doc.saveAs(file, null, options);
model.postSave();
}
// setOptions parses multiple options from a processing instruction
// and sets them via setOption
private void setOptions(String optionString, Model model) {
String s = optionString.trim();
String sName;
String sValue;
char cEquals = '=';
char cDoubleQuote = '"';
char cSingleQuote = '\'';
int nOffset = 0;
int nFoundAt;
for (;;) {
nFoundAt = s.indexOf(cEquals, nOffset);
if (nFoundAt == -1) {
// ensure that we're at the end of the string
if (nOffset != s.length())
throw new ExFull(new MsgFormat(
ResId.MalformedOptionException, optionString
.substring(nOffset)));
break;
}
sName = s.substring(nOffset, nFoundAt).trim();
if ((s.charAt(nFoundAt + 1) == '"')
|| (s.charAt(nFoundAt + 1) == '\'')) {
// find matching quote
int nFirstQuoteFoundAt = nFoundAt + 1;
int nSecondQuoteFoundAt;
if (s.charAt(nFoundAt + 1) == '"')
nSecondQuoteFoundAt = s.indexOf(cDoubleQuote,
nFirstQuoteFoundAt + 1);
else
nSecondQuoteFoundAt = s.indexOf(cSingleQuote,
nFirstQuoteFoundAt + 1);
if (nSecondQuoteFoundAt == -1)
throw new ExFull(new MsgFormat(
ResId.MalformedOptionException, sName));
sValue = s.substring(nFirstQuoteFoundAt + 1,
nSecondQuoteFoundAt);
nOffset = nSecondQuoteFoundAt + 1;
} else {
// no quote; not allowed.
throw new ExFull(new MsgFormat(ResId.MalformedOptionException,
sName));
}
// set the option. If the name starts with "xfd_", it's for this
// XFAXMLStorage
// object. Any other prefixes are invalid.
String sPackage = STRS.XFD; // package-less options are treated as
// being xfd
int nUnderscore = sName.indexOf('_');
if (nUnderscore != -1)
sPackage = sName.substring(0, nUnderscore);
if (sPackage.equals(STRS.XFD))
setOption(sName, sValue, false);
else
throw new ExFull(new MsgFormat(ResId.InvalidOptionException,
sName));
// Check for specially-handled options setPI and XFAOption.
if (mSetPIOption.isSet()) { // setPI
String optionValue = mSetPIOption.getString();
String optionName;
// Parse out the first word. This is assumed to be the name of
// the processing instruction
String[] options = optionValue.split(" ");
optionName = options[0].intern();
Document doc = ((AppModel) model).getDocument();
updateProcessingInstruction(doc, optionName, optionValue);
// reset the option
mSetPIOption.reset();
}
if (mXFAPIOption.isSet()) { // XFAOption
Document doc = ((AppModel) model).getDocument();
StringBuilder xfaValue
= new StringBuilder(compactPIs(XFA.XFA, doc));
if (xfaValue.length() > 0)
xfaValue.append(' ');
xfaValue.append(mXFAPIOption.getString());
// Bug fix TBD: this adds the option each time -- should
// replace, not add.
updateProcessingInstruction(doc, XFA.XFA, xfaValue.toString());
// reset the option
mXFAPIOption.reset();
}
}
}
public void setOption(String optionName,
String optionValue, boolean bCritical) {
Option optionArray[] = {
mGeneratorOption,
mAPIVersionOption,
mProcessXSLOption,
mXSLOption,
mModelOption,
mIncrementalOption,
mSetPIOption,
mXFAPIOption,
mFormatOption,
mEncodingOption,
mIndentLevelOption,
mAttributeQuoteCharOption,
mEntityCharsOption,
mMinEntityCharRangeOption,
mMaxEntityCharRangeOption,
mXSLFileOption,
null
};
Option.setOptionByArray("xfd", optionArray,
optionName, optionValue, bCritical);
}
// updateOptions rationalizes the user options with the saved options.
private void updateOptions(Document doc, DOMSaveOptions oOptions) {
String generatorTag = "XFA2_4";
if (doc != null) {
if (true) { // this is to delineate this hack -- NOPMD
// Unfortunately, the XPF data format is broken. There's existing
// code out there (in data/dataxml.cpp) that relies on the
// generator tag being "FF99V250_01" That code is not
// case-sensitive, so we write out the fake generator tag in
// lower-case so we can distinguish between them, if necessary.
//
// Let the record show that I fought long and hard against this
// hack. DJB Oct 2, 2000.
//
Node node = doc.getDocumentElement();
if (node instanceof DataModel) {
node = ((DataModel) node).getDataRoot();
node = ((Element) node).getFirstXMLChild();
}
if (node instanceof Element
&& ((Element) node).getXMLName() == STRS.XPFNAMESPACE)
generatorTag = "ff99v250_01";
}
//
// Update our internal generator tag
//
mGeneratorOption.setValue(generatorTag, false);
String xfaValue = compactPIs(XFA.XFA, doc);
xfaValue = updateValue(xfaValue, STRS.GENERATOR, generatorTag);
xfaValue = updateValue(xfaValue, STRS.APIVERSION,
Version.getImplementation());
updateProcessingInstruction(doc, XFA.XFA, xfaValue);
}
// Override output format based on the format option
if (mFormatOption.isSet()) {
if (mFormatOption.getMatchingIndex() == 0) // raw
oOptions.setDisplayFormat(DOMSaveOptions.RAW_OUTPUT);
else if (mFormatOption.getMatchingIndex() == 2) // pretty
oOptions.setDisplayFormat(DOMSaveOptions.PRETTY_OUTPUT);
}
if (mIndentLevelOption.isSet())
oOptions.setIndentLevel(mIndentLevelOption.getInteger());
if (mAttributeQuoteCharOption.isSet())
if (mAttributeQuoteCharOption.getMatchingIndex() == 0)
oOptions.setUseSingleQuoteAttr(true);
if (mEntityCharsOption.isSet())
oOptions.setEntityChars(mEntityCharsOption.getString());
if (mMinEntityCharRangeOption.isSet())
oOptions.setRangeMin((char) mMinEntityCharRangeOption.getInteger());
if (mMaxEntityCharRangeOption.isSet())
oOptions.setRangeMax((char) mMaxEntityCharRangeOption.getInteger());
}
public void XFAModelLoader(Model model, Node configKey,
InputStream oStreamFile) {
// TODO Auto-generated method stub
throw new ExFull(ResId.UNSUPPORTED_OPERATION, "XMLStorage#XFAModelLoader");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy