001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.cocoon.transformation;
019:
020: import net.sourceforge.chaperon.model.extended.ExtendedGrammar;
021: import net.sourceforge.chaperon.process.extended.ExtendedDirectParserProcessor;
022:
023: import org.apache.avalon.excalibur.pool.Recyclable;
024: import org.apache.avalon.framework.activity.Disposable;
025: import org.apache.avalon.framework.logger.LogEnabled;
026: import org.apache.avalon.framework.logger.Logger;
027: import org.apache.avalon.framework.parameters.ParameterException;
028: import org.apache.avalon.framework.parameters.Parameterizable;
029: import org.apache.avalon.framework.parameters.Parameters;
030: import org.apache.avalon.framework.service.ServiceException;
031: import org.apache.avalon.framework.service.ServiceManager;
032: import org.apache.avalon.framework.service.Serviceable;
033:
034: import org.apache.cocoon.ProcessingException;
035: import org.apache.cocoon.caching.CacheableProcessingComponent;
036: import org.apache.cocoon.components.source.SourceUtil;
037: import org.apache.cocoon.environment.SourceResolver;
038: import org.apache.cocoon.xml.XMLConsumer;
039:
040: import org.apache.excalibur.source.Source;
041: import org.apache.excalibur.source.SourceException;
042: import org.apache.excalibur.source.SourceValidity;
043: import org.apache.excalibur.store.Store;
044:
045: import org.exolab.castor.mapping.Mapping;
046: import org.exolab.castor.mapping.MappingException;
047: import org.exolab.castor.xml.UnmarshalHandler;
048: import org.exolab.castor.xml.Unmarshaller;
049:
050: import org.xml.sax.InputSource;
051: import org.xml.sax.SAXException;
052:
053: import java.io.IOException;
054: import java.io.Serializable;
055:
056: import java.util.Map;
057:
058: /**
059: * @author <a href="mailto:stephan@apache.org">Stephan Michels </a>
060: * @version CVS $Id: ExtendedParserTransformer.java 433543 2006-08-22 06:22:54Z crossley $
061: */
062: public class ExtendedParserTransformer extends
063: ExtendedDirectParserProcessor implements Transformer,
064: LogEnabled, Serviceable, Parameterizable, Recyclable,
065: Disposable, CacheableProcessingComponent {
066: private String grammar = null;
067: private Source grammarSource = null;
068: private Logger logger = null;
069: private ServiceManager manager = null;
070: private SourceResolver resolver = null;
071:
072: /**
073: * Provide component with a logger.
074: *
075: * @param logger the logger
076: */
077: public void enableLogging(Logger logger) {
078: this .logger = logger;
079:
080: // TODO: check if the loglevel is correct LogKitLogger -> Logger
081: //setLog(new AvalonLogger(logger));
082: }
083:
084: /**
085: * Pass the ServiceManager to the object. The Serviceable implementation
086: * should use the specified ServiceManager to acquire the services it needs
087: * for execution.
088: *
089: * @param manager The ServiceManager which this Serviceable uses.
090: */
091: public void service(ServiceManager manager) {
092: this .manager = manager;
093: }
094:
095: /**
096: * Provide component with parameters.
097: *
098: * @param parameters the parameters
099: *
100: * @throws ParameterException if parameters are invalid
101: */
102: public void parameterize(Parameters parameters)
103: throws ParameterException {
104: //setRecovery(parameters.getParameterAsBoolean("recovery", false));
105: }
106:
107: /**
108: * Set the <code>XMLConsumer</code> that will receive XML data.
109: *
110: * @param consumer
111: */
112: public void setConsumer(XMLConsumer consumer) {
113: setContentHandler(consumer);
114: setLexicalHandler(consumer);
115: }
116:
117: /**
118: * Set the SourceResolver, objectModel Map, the source and sitemap Parameters used to process the
119: * request.
120: *
121: * @param resolver Source resolver
122: * @param objectmodel Object model
123: * @param src Source
124: * @param parameters Parameters
125: *
126: * @throws IOException
127: * @throws ProcessingException
128: * @throws SAXException
129: */
130: public void setup(SourceResolver resolver, Map objectmodel,
131: String src, Parameters parameters)
132: throws ProcessingException, SAXException, IOException {
133: this .resolver = resolver;
134:
135: Store store = null;
136:
137: try {
138: this .grammar = src;
139:
140: this .grammarSource = resolver.resolveURI(this .grammar);
141:
142: // Retrieve the parser automaton from the transient store
143: store = (Store) this .manager.lookup(Store.TRANSIENT_STORE);
144:
145: GrammarEntry entry = (GrammarEntry) store
146: .get(this .grammarSource.getURI());
147:
148: // If the parser automaton has changed, rebuild the parser automaton
149: if ((entry == null)
150: || (entry.getValidity() == null)
151: || ((entry.getValidity().isValid(this .grammarSource
152: .getValidity())) <= 0)) {
153: this .logger.info("(Re)building the grammar from '"
154: + this .grammarSource.getURI() + "'");
155:
156: if (this .grammarSource.getInputStream() == null)
157: throw new ProcessingException("Source '"
158: + this .grammarSource.getURI()
159: + "' not found");
160:
161: Mapping mapping = new Mapping();
162:
163: mapping.loadMapping(new InputSource(
164: ExtendedGrammar.class
165: .getResource("mapping.xml")
166: .openStream()));
167:
168: Unmarshaller unmarshaller = new Unmarshaller(
169: ExtendedGrammar.class);
170: unmarshaller.setMapping(mapping);
171:
172: UnmarshalHandler unmarshalHandler = unmarshaller
173: .createHandler();
174: SourceUtil.toSAX(this .manager, this .grammarSource,
175: null, Unmarshaller
176: .getContentHandler(unmarshalHandler));
177:
178: ExtendedGrammar grammar = (ExtendedGrammar) unmarshalHandler
179: .getObject();
180:
181: if (grammar == null)
182: throw new ProcessingException(
183: "Error while reading the grammar from "
184: + src);
185:
186: setExtendedGrammar(grammar);
187:
188: this .logger.info("Store grammar into store for '"
189: + this .grammarSource.getURI() + "'");
190: store.store(this .grammarSource.getURI(),
191: new GrammarEntry(grammar, this .grammarSource
192: .getValidity()));
193: } else {
194: this .logger.info("Getting grammar from store for '"
195: + this .grammarSource.getURI() + "'");
196: setExtendedGrammar(entry.getExtendedGrammar());
197: }
198: } catch (MappingException me) {
199: throw new ProcessingException(
200: "Error while reading the grammar", me);
201: } catch (SourceException se) {
202: throw new ProcessingException("Error during resolving of '"
203: + src + "'.", se);
204: } catch (ServiceException se) {
205: throw new ProcessingException(
206: "Could not lookup for service", se);
207: } finally {
208: if (store != null)
209: this .manager.release(store);
210: }
211: }
212:
213: /**
214: * Generate the unique key. This key must be unique inside the space of this component.
215: *
216: * @return The generated key hashes the src
217: */
218: public Serializable getKey() {
219: return this .grammarSource.getURI();
220: }
221:
222: /**
223: * Generate the validity object.
224: *
225: * @return The generated validity object or <code>null</code> if the component is currently not
226: * cacheable.
227: */
228: public SourceValidity getValidity() {
229: return this .grammarSource.getValidity();
230: }
231:
232: /**
233: * Recycle this component. All instance variables are set to <code>null</code>.
234: */
235: public void recycle() {
236: if ((this .resolver != null) && (this .grammarSource != null)) {
237: this .resolver.release(this .grammarSource);
238: this .grammarSource = null;
239: }
240: }
241:
242: /**
243: * The dispose operation is called at the end of a components lifecycle.
244: */
245: public void dispose() {
246: if ((this .resolver != null) && (this .grammarSource != null)) {
247: this .resolver.release(this .grammarSource);
248: this .grammarSource = null;
249: }
250:
251: this .manager = null;
252: }
253:
254: /**
255: * This class represent a entry in a store to cache the parser automaton.
256: */
257: public static class GrammarEntry implements Serializable {
258: private SourceValidity validity = null;
259: private ExtendedGrammar grammar = null;
260:
261: /**
262: * Create a new entry.
263: *
264: * @param grammar Extended grammar
265: * @param validity Validity for the grammar file.
266: */
267: public GrammarEntry(ExtendedGrammar grammar,
268: SourceValidity validity) {
269: this .grammar = grammar;
270: this .validity = validity;
271: }
272:
273: /**
274: * Return the validity of the grammar file.
275: *
276: * @return Validity of the grammar file.
277: */
278: public SourceValidity getValidity() {
279: return this .validity;
280: }
281:
282: /**
283: * Return the parser automaton.
284: *
285: * @return Parser automaton.
286: */
287: public ExtendedGrammar getExtendedGrammar() {
288: return this .grammar;
289: }
290:
291: private void writeObject(java.io.ObjectOutputStream out)
292: throws IOException {
293: out.writeObject(validity);
294: out.writeObject(grammar);
295: }
296:
297: private void readObject(java.io.ObjectInputStream in)
298: throws IOException, ClassNotFoundException {
299: validity = (SourceValidity) in.readObject();
300: grammar = (ExtendedGrammar) in.readObject();
301: }
302: }
303: }
|