001: package net.sf.saxon.event;
002:
003: import net.sf.saxon.AugmentedSource;
004: import net.sf.saxon.Configuration;
005: import net.sf.saxon.StandardErrorHandler;
006: import net.sf.saxon.value.Whitespace;
007: import net.sf.saxon.om.ExternalObjectModel;
008: import net.sf.saxon.om.NamePool;
009: import net.sf.saxon.om.NodeInfo;
010: import net.sf.saxon.om.Validation;
011: import net.sf.saxon.pull.PullProvider;
012: import net.sf.saxon.pull.PullPushCopier;
013: import net.sf.saxon.pull.PullSource;
014: import net.sf.saxon.trans.DynamicError;
015: import net.sf.saxon.trans.XPathException;
016: import net.sf.saxon.type.Type;
017: import org.xml.sax.*;
018:
019: import javax.xml.transform.Source;
020: import javax.xml.transform.dom.DOMSource;
021: import javax.xml.transform.sax.SAXSource;
022: import javax.xml.transform.stream.StreamSource;
023: import java.util.List;
024:
025: /**
026: * Sender is a helper class that sends events to a Receiver from any kind of Source object
027: */
028:
029: public class Sender {
030:
031: PipelineConfiguration pipe;
032:
033: public Sender(PipelineConfiguration pipe) {
034: this .pipe = pipe;
035: }
036:
037: /**
038: * Send the contents of a Source to a Receiver. Note that if the Source
039: * identifies an element node rather than a document node, only the subtree
040: * rooted at that element will be copied.
041: * @param source the document or element to be copied
042: * @param receiver the destination to which it is to be copied
043: */
044:
045: public void send(Source source, Receiver receiver)
046: throws XPathException {
047: send(source, receiver, false);
048: }
049:
050: /**
051: * Send the contents of a Source to a Receiver. Note that if the Source
052: * identifies an element node rather than a document node, only the subtree
053: * rooted at that element will be copied.
054: * @param source the document or element to be copied
055: * @param receiver the destination to which it is to be copied
056: * @param isFinal set to true when the document is being processed purely for the
057: * sake of validation, in which case multiple validation errors in the source can be
058: * reported.
059: */
060:
061: public void send(Source source, Receiver receiver, boolean isFinal)
062: throws XPathException {
063: Configuration config = pipe.getConfiguration();
064: receiver.setPipelineConfiguration(pipe);
065: receiver.setSystemId(source.getSystemId());
066:
067: int stripSpace = Whitespace.UNSPECIFIED;
068:
069: int validation = config.getSchemaValidationMode();
070: if (isFinal) {
071: // this ensures that the Validate command produces multiple error messages
072: validation |= Validation.VALIDATE_OUTPUT;
073: }
074:
075: XMLReader parser = null;
076: if (source instanceof AugmentedSource) {
077: stripSpace = ((AugmentedSource) source).getStripSpace();
078: int localValidate = ((AugmentedSource) source)
079: .getSchemaValidation();
080: if (localValidate != Validation.DEFAULT) {
081: validation = localValidate;
082: }
083: parser = ((AugmentedSource) source).getXMLReader();
084:
085: List filters = ((AugmentedSource) source).getFilters();
086: if (filters != null) {
087: for (int i = filters.size() - 1; i >= 0; i--) {
088: ProxyReceiver filter = (ProxyReceiver) filters
089: .get(i);
090: filter.setPipelineConfiguration(pipe);
091: filter.setSystemId(source.getSystemId());
092: filter.setUnderlyingReceiver(receiver);
093: receiver = filter;
094: }
095: }
096:
097: source = ((AugmentedSource) source).getContainedSource();
098: }
099:
100: if (source instanceof NodeInfo) {
101: NodeInfo ns = (NodeInfo) source;
102: String baseURI = ns.getBaseURI();
103: int val = validation & Validation.VALIDATION_MODE_MASK;
104: if (val != Validation.PRESERVE) {
105: receiver = config.getDocumentValidator(receiver,
106: baseURI, config.getNamePool(), val,
107: Whitespace.NONE, null);
108: }
109:
110: int kind = ns.getNodeKind();
111: if (kind != Type.DOCUMENT && kind != Type.ELEMENT) {
112: throw new IllegalArgumentException(
113: "Sender can only handle document or element nodes");
114: }
115: receiver.setSystemId(baseURI);
116: sendDocumentInfo(ns, receiver);
117: return;
118:
119: } else if (source instanceof PullSource) {
120: sendPullSource((PullSource) source, receiver, validation);
121: return;
122:
123: } else if (source instanceof SAXSource) {
124: sendSAXSource((SAXSource) source, receiver, validation,
125: stripSpace);
126: return;
127:
128: } else if (source instanceof StreamSource) {
129: StreamSource ss = (StreamSource) source;
130: String url = source.getSystemId();
131: InputSource is = new InputSource(url);
132: is.setCharacterStream(ss.getReader());
133: is.setByteStream(ss.getInputStream());
134: boolean reuseParser = false;
135: if (parser == null) {
136: parser = config.getSourceParser();
137: reuseParser = true;
138: }
139: SAXSource sax = new SAXSource(parser, is);
140: sax.setSystemId(source.getSystemId());
141: sendSAXSource(sax, receiver, validation, stripSpace);
142: if (reuseParser) {
143: config.reuseSourceParser(parser);
144: }
145: return;
146: } else {
147: if ((validation & Validation.VALIDATION_MODE_MASK) != Validation.PRESERVE) {
148: // Add a document validator to the pipeline
149: receiver = config.getDocumentValidator(receiver, source
150: .getSystemId(), config.getNamePool(),
151: validation, stripSpace, null);
152: }
153:
154: // See if there is a registered SourceResolver than can handle it
155: Source newSource = config.getSourceResolver()
156: .resolveSource(source, config);
157: if (newSource instanceof StreamSource
158: || newSource instanceof SAXSource
159: || newSource instanceof NodeInfo
160: || newSource instanceof PullSource) {
161: send(newSource, receiver, isFinal);
162: }
163:
164: // See if there is a registered external object model that knows about this kind of source
165:
166: List externalObjectModels = config
167: .getExternalObjectModels();
168: for (int m = 0; m < externalObjectModels.size(); m++) {
169: ExternalObjectModel model = (ExternalObjectModel) externalObjectModels
170: .get(m);
171: boolean done = model.sendSource(source, receiver, pipe);
172: if (done) {
173: return;
174: }
175: }
176:
177: }
178: if (source instanceof DOMSource) {
179: throw new DynamicError(
180: "DOMSource cannot be processed: check that saxon8-dom.jar is on the classpath");
181: }
182: throw new DynamicError("A source of type "
183: + source.getClass().getName()
184: + " is not supported in this environment");
185: }
186:
187: private void sendDocumentInfo(NodeInfo top, Receiver receiver)
188: throws XPathException {
189: NamePool targetNamePool = pipe.getConfiguration().getNamePool();
190: if (top.getNamePool() != targetNamePool) {
191: // This happens for example when turning an arbitrary DocumentInfo tree into a stylesheet
192: NamePoolConverter converter = new NamePoolConverter(top
193: .getNamePool(), targetNamePool);
194: converter.setUnderlyingReceiver(receiver);
195: //PipelineConfiguration newpipe = pipe.getConfiguration().makePipelineConfiguration();
196:
197: converter.setPipelineConfiguration(receiver
198: .getPipelineConfiguration());
199: receiver = converter;
200: }
201: DocumentSender sender = new DocumentSender(top);
202: sender.send(receiver);
203: }
204:
205: private void sendSAXSource(SAXSource source, Receiver receiver,
206: int validation, int stripSpace) throws XPathException {
207: XMLReader parser = source.getXMLReader();
208: boolean reuseParser = false;
209: final Configuration config = pipe.getConfiguration();
210: boolean checkXmlVersion = (config.getXMLVersion() == Configuration.XML10);
211: if (parser == null) {
212: SAXSource ss = new SAXSource();
213: ss.setInputSource(source.getInputSource());
214: ss.setSystemId(source.getSystemId());
215: parser = config.getSourceParser();
216: ss.setXMLReader(parser);
217: source = ss;
218: reuseParser = true;
219: if (!config.mustCheckForXML11Input()) {
220: checkXmlVersion = false;
221: }
222: } else if (config.mustCheckForXML11Input()) {
223: try {
224: boolean value = parser
225: .getFeature("http://xml.org/sax/features/xml-1.1");
226: if (value) {
227: checkXmlVersion = true;
228: }
229: } catch (SAXNotRecognizedException e) {
230: //
231: } catch (SAXNotSupportedException e) {
232: //
233: }
234: }
235:
236: //if (parser.getErrorHandler()==null) {
237: parser.setErrorHandler(new StandardErrorHandler(pipe
238: .getErrorListener()));
239: //}
240:
241: try {
242: parser.setFeature("http://xml.org/sax/features/namespaces",
243: true);
244: } catch (SAXNotSupportedException err) { // SAX2 parsers MUST support this feature!
245: throw new DynamicError(
246: "The SAX2 parser does not recognize the 'namespaces' feature");
247: } catch (SAXNotRecognizedException err) {
248: throw new DynamicError(
249: "The SAX2 parser does not support setting the 'namespaces' feature to true");
250: }
251:
252: try {
253: parser.setFeature(
254: "http://xml.org/sax/features/namespace-prefixes",
255: false);
256: } catch (SAXNotSupportedException err) { // SAX2 parsers MUST support this feature!
257: throw new DynamicError(
258: "The SAX2 parser does not recognize the 'namespace-prefixes' feature");
259: } catch (SAXNotRecognizedException err) {
260: throw new DynamicError(
261: "The SAX2 parser does not support setting the 'namespace-prefixes' feature to false");
262: }
263:
264: if ((validation & Validation.VALIDATION_MODE_MASK) != Validation.PRESERVE) {
265: // Add a document validator to the pipeline
266: receiver = config.getDocumentValidator(receiver, source
267: .getSystemId(), config.getNamePool(), validation,
268: stripSpace, null);
269: }
270:
271: ReceivingContentHandler ce = new ReceivingContentHandler();
272: ce.setReceiver(receiver);
273: ce.setPipelineConfiguration(pipe);
274: if (stripSpace == Whitespace.IGNORABLE) {
275: ce.setIgnoreIgnorableWhitespace(true);
276: } else if (stripSpace == Whitespace.NONE) {
277: ce.setIgnoreIgnorableWhitespace(false);
278: }
279: parser.setContentHandler(ce);
280: parser.setDTDHandler(ce);
281:
282: try {
283: parser
284: .setProperty(
285: "http://xml.org/sax/properties/lexical-handler",
286: ce);
287: } catch (SAXNotSupportedException err) { // this just means we won't see the comments
288: } catch (SAXNotRecognizedException err) {
289: }
290:
291: try {
292: parser.parse(source.getInputSource());
293: } catch (SAXException err) {
294: Exception nested = err.getException();
295: if (nested instanceof XPathException) {
296: throw (XPathException) nested;
297: } else if (nested instanceof RuntimeException) {
298: throw (RuntimeException) nested;
299: } else {
300: DynamicError de = new DynamicError(err);
301: de.setHasBeenReported();
302: throw de;
303: }
304: } catch (java.io.IOException err) {
305: throw new DynamicError(err);
306: }
307: if (reuseParser) {
308: config.reuseSourceParser(parser);
309: }
310: }
311:
312: private void sendPullSource(PullSource source, Receiver receiver,
313: int validation) throws XPathException {
314: if (validation != Validation.PRESERVE
315: && validation != Validation.STRIP) {
316: throw new DynamicError(
317: "Validation is not currently supported with a PullSource");
318: }
319: receiver.open();
320: PullProvider provider = source.getPullProvider();
321: provider.setPipelineConfiguration(pipe);
322: receiver.setPipelineConfiguration(pipe);
323: PullPushCopier copier = new PullPushCopier(provider, receiver);
324: copier.copy();
325: receiver.close();
326: }
327:
328: }
329:
330: //
331: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
332: // you may not use this file except in compliance with the License. You may obtain a copy of the
333: // License at http://www.mozilla.org/MPL/
334: //
335: // Software distributed under the License is distributed on an "AS IS" basis,
336: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
337: // See the License for the specific language governing rights and limitations under the License.
338: //
339: // The Original Code is: all this file.
340: //
341: // The Initial Developer of the Original Code is Michael H. Kay
342: //
343: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
344: //
345: // Contributor(s): none.
346: //
|