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

com.adobe.xfa.service.storage.XMLStorage 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.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