001: /*
002: * AbstractWingTransformer.java
003: *
004: * Version: $Revision: 1.15 $
005: *
006: * Date: $Date: 2006/06/02 21:48:02 $
007: *
008: * Copyright (c) 2002, Hewlett-Packard Company and Massachusetts
009: * Institute of Technology. All rights reserved.
010: *
011: * Redistribution and use in source and binary forms, with or without
012: * modification, are permitted provided that the following conditions are
013: * met:
014: *
015: * - Redistributions of source code must retain the above copyright
016: * notice, this list of conditions and the following disclaimer.
017: *
018: * - Redistributions in binary form must reproduce the above copyright
019: * notice, this list of conditions and the following disclaimer in the
020: * documentation and/or other materials provided with the distribution.
021: *
022: * - Neither the name of the Hewlett-Packard Company nor the name of the
023: * Massachusetts Institute of Technology nor the names of their
024: * contributors may be used to endorse or promote products derived from
025: * this software without specific prior written permission.
026: *
027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
028: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
029: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
030: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
031: * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
032: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
033: * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
034: * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
035: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
036: * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
037: * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
038: * DAMAGE.
039: */
040:
041: package org.dspace.app.xmlui.wing;
042:
043: import java.util.Stack;
044:
045: import org.apache.cocoon.transformation.AbstractTransformer;
046: import org.dspace.app.xmlui.wing.element.Body;
047: import org.dspace.app.xmlui.wing.element.Options;
048: import org.dspace.app.xmlui.wing.element.PageMeta;
049: import org.dspace.app.xmlui.wing.element.UserMeta;
050: import org.dspace.app.xmlui.wing.element.WingDocument;
051: import org.dspace.app.xmlui.wing.element.WingMergeableElement;
052: import org.xml.sax.Attributes;
053: import org.xml.sax.SAXException;
054: import org.xml.sax.helpers.NamespaceSupport;
055:
056: /**
057: * This class handles receiving SAX events and translating them into DRI events.
058: * These DRI events are then routed to the individual implementing components
059: * where they fill in and construct the DRI document. The document they
060: * construct is known as the feeder document, this is merged into the main
061: * document that was generated from the previous component in the Cocoon
062: * pipeline. The merge takes place in accordance with the DRI schema's rules for
063: * merging two DRI documents.
064: *
065: *
066: * @author Scott Phillips
067: */
068: public abstract class AbstractWingTransformer extends
069: AbstractTransformer implements WingTransformer {
070: /**
071: * Simple variable to indicate weather a new namespace context is needed. If
072: * several namespaces are declared on the same attribute then they are
073: * considered in the same 'context'. Each time an element is opened this
074: * flag is reset to true, and each time a new namespace is declared it is
075: * set to false. Using this information new contexts are opened
076: * conservatively.
077: */
078: private boolean needNewNamespaceContext = true;
079:
080: /**
081: * The namespace support object keeps track of registered URI prefixes. This
082: * is used by the WingElements so that they may attach the correctp prefix
083: * when assigning elements to namespaces.
084: */
085: private NamespaceSupport namespaces;
086:
087: /**
088: * The feeder document is the document being merged into the main,
089: * pre-existing document, that is the result of the previous Cocoon
090: * component in the pipeline.
091: */
092: private WingDocument feederDocument;
093:
094: /**
095: * The wing context is where the namespace support is stored along with the
096: * content and lexical handlers so that the wing elements can have access to
097: * them when they perform their toSAX() method.
098: */
099: private WingContext wingContext;
100:
101: /**
102: * This is a stack to the current location in the merge while it is in
103: * progress.
104: */
105: private Stack<WingMergeableElement> stack;
106:
107: /**
108: * Set up the transformer so that it can build a feeder Wing document and
109: * merge it into the main document
110: *
111: * FIXME: Update document: - this method must be called to initialize the
112: * framework. It must be called after the component's setup has been called
113: * and the implementing object setup.
114: *
115: */
116: public void setupWing() throws WingException {
117: this .wingContext = new WingContext();
118: this .wingContext.setLogger(this .getLogger());
119: this .wingContext.setComponentName(this .getComponentName());
120: this .wingContext.setObjectManager(this .getObjectManager());
121:
122: feederDocument = this .createWingDocument(wingContext);
123: this .stack = new Stack<WingMergeableElement>();
124: }
125:
126: /**
127: * Receive notification of the beginning of a document.
128: */
129: public void startDocument() throws SAXException {
130: needNewNamespaceContext = true;
131: namespaces = new NamespaceSupport();
132:
133: super .startDocument();
134: }
135:
136: /**
137: * Receive notification of the end of a document.
138: */
139: public void endDocument() throws SAXException {
140: wingContext.dispose();
141: super .endDocument();
142: }
143:
144: /**
145: * Begin the scope of a prefix-URI Namespace mapping.
146: *
147: * @param prefix
148: * The Namespace prefix being declared.
149: * @param uri
150: * The Namespace URI the prefix is mapped to.
151: */
152: public void startPrefixMapping(String prefix, String uri)
153: throws SAXException {
154: if (needNewNamespaceContext) {
155: namespaces.pushContext();
156: needNewNamespaceContext = false;
157: }
158: namespaces.declarePrefix(prefix, uri);
159:
160: super .startPrefixMapping(prefix, uri);
161: }
162:
163: /**
164: * End the scope of a prefix-URI mapping.
165: *
166: * @param prefix
167: * The prefix that was being mapping.
168: */
169: public void endPrefixMapping(String prefix) throws SAXException {
170: if (!needNewNamespaceContext) {
171: namespaces.popContext();
172: needNewNamespaceContext = true;
173: }
174: super .endPrefixMapping(prefix);
175: }
176:
177: /**
178: * Receive notification of the beginning of an element.
179: *
180: * @param namespaceURI
181: * The Namespace URI, or the empty string if the element has no
182: * Namespace URI or if Namespace processing is not being
183: * performed.
184: * @param localName
185: * The local name (without prefix), or the empty string if
186: * Namespace processing is not being performed.
187: * @param qName
188: * The raw XML 1.0 name (with prefix), or the empty string if raw
189: * names are not available.
190: * @param attributes
191: * The attributes attached to the element. If there are no
192: * attributes, it shall be an empty Attributes object.
193: */
194: public void startElement(String namespaceURI, String localName,
195: String qName, Attributes attributes) throws SAXException {
196: // Reset the namespace context flag.
197: needNewNamespaceContext = true;
198:
199: try {
200: if (stack == null) {
201: throw new WingException("Stack not initialized.");
202: }
203:
204: // Deal with the stack jump start issue of having a document all
205: // ready on the stack.
206: if (stack.size() == 0) {
207: if (feederDocument.mergeEqual(namespaceURI, localName,
208: qName, attributes)) {
209: attributes = feederDocument.merge(attributes);
210: stack.push(feederDocument);
211: } else {
212: throw new WingException(
213: "Attempting to merge DRI documents but the source document is not compatable with the feeder document.");
214: }
215:
216: } else if (stack.size() > 0) {
217: WingMergeableElement peek = stack.peek();
218: WingMergeableElement child = null;
219: if (peek != null) {
220: child = peek.mergeChild(namespaceURI, localName,
221: qName, attributes);
222: }
223:
224: // Check if we should construct a new portion of the document.
225: if (child instanceof UserMeta) {
226: // Create the UserMeta
227: this .addUserMeta((UserMeta) child);
228: } else if (child instanceof PageMeta) {
229: // Create the PageMeta
230: this .addPageMeta((PageMeta) child);
231: } else if (child instanceof Body) {
232: // Create the Body
233: this .addBody((Body) child);
234: } else if (child instanceof Options) {
235: // Create the Options
236: this .addOptions((Options) child);
237: }
238:
239: // Update any attributes of this merged element.
240: if (child != null)
241: attributes = child.merge(attributes);
242: stack.push(child);
243: }
244: // Send off the event with nothing modified except for the
245: // attributes (possibly)
246: super .startElement(namespaceURI, localName, qName,
247: attributes);
248: } catch (SAXException saxe) {
249: throw saxe;
250: } catch (Exception e) {
251: handleException(e);
252: }
253:
254: }
255:
256: /**
257: * Receive notification of the end of an element.
258: *
259: * @param namespaceURI
260: * The Namespace URI, or the empty string if the element has no
261: * Namespace URI or if Namespace processing is not being
262: * performed.
263: * @param localName
264: * The local name (without prefix), or the empty string if
265: * Namespace processing is not being performed.
266: * @param qName
267: * The raw XML 1.0 name (with prefix), or the empty string if raw
268: * names are not available.
269: */
270: public void endElement(String namespaceURI, String localName,
271: String qName) throws SAXException {
272: try {
273: if (stack.size() > 0) {
274: WingMergeableElement poped = stack.pop();
275: if (poped != null) {
276: poped.toSAX(contentHandler, lexicalHandler,
277: namespaces);
278: poped.dispose();
279: }
280: }
281:
282: // Send the event on unmodified
283: super .endElement(namespaceURI, localName, qName);
284: } catch (SAXException saxe) {
285: throw saxe;
286: } catch (Exception e) {
287: handleException(e);
288: }
289: }
290:
291: /**
292: * Handle exceptions that occurred during the document's creation. When
293: * errors occur a SAX event is being processed it will be sent through this
294: * method. This allows implementing classes to override this method for
295: * specific error handling hooks.
296: *
297: * @param e
298: * The thrown exception
299: */
300:
301: protected void handleException(Exception e) throws SAXException {
302: throw new SAXException(
303: "An error was incountered while processing the Wing based component: "
304: + this .getClass().getName(), e);
305: }
306:
307: /**
308: * Construct a new WingDocument.
309: *
310: * @param wingContext
311: * The current wing context this transformer is operating under.
312: */
313: protected WingDocument createWingDocument(WingContext wingContext)
314: throws WingException {
315: return new WingDocument(wingContext);
316: }
317:
318: /** Abstract implementations of WingTransformer */
319:
320: public void addBody(Body body) throws Exception {
321: // Do nothing
322: }
323:
324: public void addOptions(Options options) throws Exception {
325: // do nothing
326: }
327:
328: public void addUserMeta(UserMeta userMeta) throws Exception {
329: // Do nothing
330: }
331:
332: public void addPageMeta(PageMeta pageMeta) throws Exception {
333: // Do nothing
334: }
335:
336: /**
337: * Return the ObjectManager associated with this component. If no
338: * objectManager needed then return null.
339: */
340: public ObjectManager getObjectManager() {
341: return null;
342: }
343:
344: /**
345: * Return the name of this component. Typicaly the name is just
346: * the class name of the component.
347: */
348: public String getComponentName() {
349: return this .getClass().getName();
350: }
351:
352: /**
353: * Return the default i18n message catalogue that should be used
354: * when no others are specified.
355: */
356: protected static String getDefaultMessageCatalogue() {
357: return "default";
358: }
359:
360: /**
361: * This is a short cut method for creating a new message object, this
362: * allows them to be created with one simple method call that uses
363: * the default catalogue.
364: *
365: * @param key
366: * The catalogue key used to look up a message.
367: * @return A new message object.
368: */
369: public static Message message(String key) {
370: return message(getDefaultMessageCatalogue(), key);
371: }
372:
373: /**
374: * This is a short cut method for creating a new message object. This
375: * version allows the callie to specify a particular catalogue overriding
376: * the default catalogue supplied.
377: *
378: * @param catalogue
379: * The catalogue where translations will be located.
380: * @param key
381: * The catalogue key used to look up a translation within the
382: * catalogue.
383: * @return A new message object.
384: */
385: public static Message message(String catalogue, String key) {
386: return new Message(catalogue, key);
387: }
388:
389: /**
390: * Recyle
391: */
392: public void recycle() {
393: this .namespaces = null;
394: this .feederDocument = null;
395: this .wingContext = null;
396: this .stack = null;
397: super .recycle();
398: }
399:
400: /**
401: * Dispose
402: */
403: public void dispose() {
404: this .namespaces = null;
405: this .feederDocument = null;
406: this .wingContext = null;
407: this .stack = null;
408: //super.dispose(); super dosn't dispose.
409: }
410:
411: }
|