001: package net.sf.saxon.dom;
002:
003: import net.sf.saxon.Configuration;
004: import net.sf.saxon.event.PipelineConfiguration;
005: import net.sf.saxon.event.Receiver;
006: import net.sf.saxon.event.Sender;
007: import net.sf.saxon.expr.XPathContext;
008: import net.sf.saxon.om.*;
009: import net.sf.saxon.trans.DynamicError;
010: import net.sf.saxon.trans.XPathException;
011: import net.sf.saxon.value.SequenceExtent;
012: import net.sf.saxon.value.Value;
013: import org.w3c.dom.Node;
014: import org.w3c.dom.NodeList;
015:
016: import javax.xml.transform.Result;
017: import javax.xml.transform.Source;
018: import javax.xml.transform.dom.DOMSource;
019: import java.io.Serializable;
020: import java.util.ArrayList;
021: import java.util.HashSet;
022: import java.util.List;
023:
024: /**
025: * DOMEnvelope is an object model representation in which DOM interfaces are wrapped around
026: * Saxon NodeInfo nodes: that is, it implements the DOM on top of a Saxon tree implementation
027: * such as the TinyTree or standard tree.
028: */
029:
030: //TODO: this class has been supplied to a user (Pier Fumagalli) for testing. If it works, the code in
031: //DOMObjectModel that handles a NodeOverNodeInfo wrapper is redundant and can be removed.
032: public class DOMEnvelope implements ExternalObjectModel, Serializable {
033:
034: public DOMEnvelope() {
035: }
036:
037: /**
038: * Test whether this object model recognizes a given node as one of its own
039: */
040:
041: public boolean isRecognizedNode(Object object) {
042: return object instanceof NodeOverNodeInfo;
043: }
044:
045: /**
046: * Test whether this object model recognizes a given class as representing a
047: * node in that object model. This method will generally be called at compile time.
048: *
049: * @param nodeClass A class that possibly represents nodes
050: * @return true if the class is used to represent nodes in this object model
051: */
052:
053: public boolean isRecognizedNodeClass(Class nodeClass) {
054: return NodeOverNodeInfo.class.isAssignableFrom(nodeClass);
055: }
056:
057: /**
058: * Test whether this object model recognizes a given class as representing a
059: * list of nodes in that object model. This method will generally be called at compile time.
060: *
061: * @param nodeClass A class that possibly represents nodes
062: * @return true if the class is used to represent nodes in this object model
063: */
064:
065: public boolean isRecognizedNodeListClass(Class nodeClass) {
066: return NodeList.class.isAssignableFrom(nodeClass);
067: }
068:
069: /**
070: * Test whether this object model recognizes a particular kind of JAXP Result object,
071: * and if it does, return a Receiver that builds an instance of this data model from
072: * a sequence of events. If the Result is not recognised, return null.
073: * <p>
074: * This implementation always returns null: it is not possible to construct an instance
075: * of this object model implementation directly as the result of a JAXP transformation.
076: */
077:
078: public Receiver getDocumentBuilder(Result result)
079: throws XPathException {
080: return null;
081: }
082:
083: /**
084: * Test whether this object model recognizes a particular kind of JAXP Source object,
085: * and if it does, send the contents of the document to a supplied Receiver, and return true.
086: * Otherwise, return false.
087: * <p>
088: * This implementation returns true only if the source is a DOMSource whose contained node is a
089: * a "NodeOverNodeInfo".
090: */
091:
092: public boolean sendSource(Source source, Receiver receiver,
093: PipelineConfiguration pipe) throws XPathException {
094: if (source instanceof DOMSource) {
095: Node startNode = ((DOMSource) source).getNode();
096: if (startNode instanceof NodeOverNodeInfo) {
097: NodeInfo base = ((NodeOverNodeInfo) startNode)
098: .getUnderlyingNodeInfo();
099: Sender driver = new Sender(pipe);
100: driver.send(base, receiver);
101: return true;
102: }
103: }
104: return false;
105: }
106:
107: /**
108: * Wrap or unwrap a node using this object model to return the corresponding Saxon node. If the supplied
109: * source does not belong to this object model, return null
110: */
111:
112: public NodeInfo unravel(Source source, Configuration config) {
113:
114: if (source instanceof DOMSource) {
115: NodeInfo start;
116: Node dsnode = ((DOMSource) source).getNode();
117: if (dsnode instanceof NodeOverNodeInfo) {
118: // Supplied source is a DOM Node wrapping a Saxon node: unwrap it
119: start = ((NodeOverNodeInfo) dsnode)
120: .getUnderlyingNodeInfo();
121: return start;
122: }
123: }
124: return null;
125: }
126:
127: /**
128: * Convert a Java object to an XPath value. If the supplied object is recognized as a representation
129: * of a value using this object model, the object model should convert the value to an XPath value
130: * and return this as the result. If not, it should return null. If the object is recognized but cannot
131: * be converted, an exception should be thrown
132: */
133:
134: public Value convertObjectToXPathValue(Object object,
135: Configuration config) throws XPathException {
136: if (object instanceof NodeList) {
137: NodeList list = ((NodeList) object);
138: NodeInfo[] nodes = new NodeInfo[list.getLength()];
139: for (int i = 0; i < list.getLength(); i++) {
140: if (list.item(i) instanceof NodeOverNodeInfo) {
141: nodes[i] = ((NodeOverNodeInfo) list.item(i))
142: .getUnderlyingNodeInfo();
143: } else {
144: return null;
145: }
146: }
147: return new SequenceExtent(nodes);
148:
149: // Note, we accept the nodes in the order returned by the function; there
150: // is no requirement that this should be document order.
151: } else if (object instanceof NodeOverNodeInfo) {
152: return Value.asValue(((NodeOverNodeInfo) object)
153: .getUnderlyingNodeInfo());
154: } else {
155: return null;
156: }
157: }
158:
159: /**
160: * Convert an XPath value to an object in this object model. If the supplied value can be converted
161: * to an object in this model, of the specified class, then the conversion should be done and the
162: * resulting object returned. If the value cannot be converted, the method should return null. Note
163: * that the supplied class might be a List, in which case the method should inspect the contents of the
164: * Value to see whether they belong to this object model.
165: * @throws net.sf.saxon.trans.XPathException if the target class is explicitly associated with this object model, but the
166: * supplied value cannot be converted to the appropriate class
167: */
168:
169: public Object convertXPathValueToObject(Value value, Class target,
170: XPathContext context) throws XPathException {
171: // We accept the object if (a) the target class is Node, Node[], or NodeList,
172: // or (b) the supplied object is a node, or sequence of nodes, that wrap DOM nodes,
173: // provided that the target class is Object or a collection class
174: boolean requireDOM = (Node.class.isAssignableFrom(target)
175: || (target == NodeList.class) || (target.isArray() && Node.class
176: .isAssignableFrom(target.getComponentType())));
177:
178: // Note: we allow the declared type of the method argument to be a subclass of Node. If the actual
179: // node supplied is the wrong kind of node, this will result in a Java exception.
180:
181: boolean allowDOM = (target == Object.class
182: || target.isAssignableFrom(ArrayList.class)
183: || target.isAssignableFrom(HashSet.class) || (target
184: .isArray() && target.getComponentType() == Object.class));
185: if (!(requireDOM || allowDOM)) {
186: return null;
187: }
188: List nodes = new ArrayList(20);
189:
190: SequenceIterator iter = value.iterate(context);
191: while (true) {
192: Item item = iter.next();
193: if (item == null) {
194: break;
195: }
196: if (item instanceof VirtualNode) {
197: Object o = ((VirtualNode) item).getUnderlyingNode();
198: if (o instanceof Node) {
199: nodes.add(o);
200: } else {
201: if (requireDOM) {
202: DynamicError err = new DynamicError(
203: "Extension function required class "
204: + target.getName()
205: + "; supplied value of class "
206: + item.getClass().getName()
207: + " could not be converted");
208: throw err;
209: }
210: ;
211: }
212: } else if (requireDOM) {
213: if (item instanceof NodeInfo) {
214: nodes.add(NodeOverNodeInfo.wrap((NodeInfo) item));
215: } else {
216: DynamicError err = new DynamicError(
217: "Extension function required class "
218: + target.getName()
219: + "; supplied value of class "
220: + item.getClass().getName()
221: + " could not be converted");
222: throw err;
223: }
224: } else {
225: return null; // DOM Nodes are not actually required; let someone else try the conversion
226: }
227: }
228:
229: if (nodes.size() == 0 && !requireDOM) {
230: return null; // empty sequence supplied - try a different mapping
231: }
232: if (Node.class.isAssignableFrom(target)) {
233: if (nodes.size() != 1) {
234: DynamicError err = new DynamicError(
235: "Extension function requires a single DOM Node"
236: + "; supplied value contains "
237: + nodes.size() + " nodes");
238: throw err;
239: }
240: return nodes.get(0);
241: // could fail if the node is of the wrong kind
242: } else if (target == NodeList.class) {
243: return new DOMNodeList(nodes);
244: } else if (target.isArray()
245: && target.getComponentType() == Node.class) {
246: Node[] array = new Node[nodes.size()];
247: nodes.toArray(array);
248: return array;
249: } else if (target.isAssignableFrom(ArrayList.class)) {
250: return nodes;
251: } else if (target.isAssignableFrom(HashSet.class)) {
252: return new HashSet(nodes);
253: } else {
254: // after all this work, give up
255: return null;
256: }
257: }
258:
259: /**
260: * Wrap a document node in the external object model in a document wrapper that implements
261: * the Saxon DocumentInfo interface. (However, if the supplied object is a wrapper for a Saxon
262: * NodeInfo object, which is the case for this object model, then we <i>unwrap</i> it).
263: * @param node a node (any node) in the third party document
264: * @param baseURI the base URI of the node (supply "" if unknown)
265: * @param config the Saxon configuration (which among other things provides access to the NamePool)
266: * @return the wrapper, which must implement DocumentInfo
267: */
268:
269: public DocumentInfo wrapDocument(Object node, String baseURI,
270: Configuration config) {
271: if (node instanceof DocumentOverNodeInfo) {
272: return (DocumentInfo) ((DocumentOverNodeInfo) node)
273: .getUnderlyingNodeInfo();
274: }
275: if (node instanceof NodeOverNodeInfo) {
276: return ((NodeOverNodeInfo) node).getUnderlyingNodeInfo()
277: .getDocumentRoot();
278: }
279: throw new IllegalArgumentException("Unknown node class "
280: + node.getClass());
281: }
282:
283: /**
284: * Wrap a node in the external object model in a wrapper that implements
285: * the Saxon NodeInfo interface. (However, if the supplied object is a wrapper for a Saxon
286: * NodeInfo object, which is the case for this object model, then we <i>unwrap</i> it).
287: * @param document the document wrapper, as a DocumentInfo object
288: * @param node the node to be wrapped. This must be a node within the document wrapped by the
289: * DocumentInfo provided in the first argument
290: * @return the wrapper for the node, as an instance of VirtualNode
291: */
292:
293: public NodeInfo wrapNode(DocumentInfo document, Object node) {
294: if (node instanceof NodeOverNodeInfo) {
295: return ((NodeOverNodeInfo) node).getUnderlyingNodeInfo();
296: }
297: throw new IllegalArgumentException("Unknown node class "
298: + node.getClass());
299: }
300:
301: /**
302: * Convert a sequence of values to a NODELIST, as defined in the JAXP XPath API spec. This method
303: * is used when the evaluate() request specifies the return type as NODELIST, regardless of the
304: * actual results of the expression. If the sequence contains things other than nodes, the fallback
305: * is to return the sequence as a Java List object. The method can return null to invoke fallback
306: * behaviour.
307: */
308:
309: public Object convertToNodeList(SequenceExtent extent) {
310: return null;
311: }
312: }
313:
314: //
315: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
316: // you may not use this file except in compliance with the License. You may obtain a copy of the
317: // License at http://www.mozilla.org/MPL/
318: //
319: // Software distributed under the License is distributed on an "AS IS" basis,
320: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
321: // See the License for the specific language governing rights and limitations under the License.
322: //
323: // The Original Code is: all this file.
324: //
325: // The Initial Developer of the Original Code is Michael H. Kay.
326: //
327: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
328: //
329: // Contributor(s): Gunther Schadow (changes to allow access to public fields; also wrapping
330: // of extensions and mapping of null to empty sequence).
331: //
|