Permalink
Browse files

Initial impport of Butterfly

git-svn-id: https://svn.apache.org/repos/asf/cocoon/branches/src@30863 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information...
0 parents commit 8e26b70fba14b7f8cc7573ecbd40177aee4fd193 Ugo Cei committed Jul 28, 2004
Showing with 5,374 additions and 0 deletions.
  1. +55 −0 java/org/apache/butterfly/components/pipeline/InvalidPipelineException.java
  2. +55 −0 java/org/apache/butterfly/components/pipeline/PipelineException.java
  3. +55 −0 java/org/apache/butterfly/components/pipeline/PipelineProcessingException.java
  4. +132 −0 java/org/apache/butterfly/components/pipeline/ProcessingPipeline.java
  5. +265 −0 java/org/apache/butterfly/components/pipeline/impl/NonCachingProcessingPipeline.java
  6. +191 −0 java/org/apache/butterfly/environment/Environment.java
  7. +64 −0 java/org/apache/butterfly/generation/FileGenerator.java
  8. +29 −0 java/org/apache/butterfly/generation/Generator.java
  9. +41 −0 java/org/apache/butterfly/reading/Reader.java
  10. +29 −0 java/org/apache/butterfly/serialization/Serializer.java
  11. +125 −0 java/org/apache/butterfly/serialization/XMLSerializer.java
  12. +43 −0 java/org/apache/butterfly/sitemap/SitemapOutputComponent.java
  13. +143 −0 java/org/apache/butterfly/source/Source.java
  14. +42 −0 java/org/apache/butterfly/source/SourceException.java
  15. +53 −0 java/org/apache/butterfly/source/SourceFactory.java
  16. +42 −0 java/org/apache/butterfly/source/SourceNotFoundException.java
  17. +120 −0 java/org/apache/butterfly/source/SourceResolver.java
  18. +473 −0 java/org/apache/butterfly/source/SourceUtil.java
  19. +75 −0 java/org/apache/butterfly/source/SourceValidity.java
  20. +33 −0 java/org/apache/butterfly/source/URIAbsolutizer.java
  21. +176 −0 java/org/apache/butterfly/source/impl/FileSource.java
  22. +50 −0 java/org/apache/butterfly/source/impl/FileSourceFactory.java
  23. +109 −0 java/org/apache/butterfly/source/impl/URLSource.java
  24. +39 −0 java/org/apache/butterfly/source/impl/URLSourceFactory.java
  25. +87 −0 java/org/apache/butterfly/source/impl/validity/FileTimeStampValidity.java
  26. +28 −0 java/org/apache/butterfly/transformation/Transformer.java
  27. +68 −0 java/org/apache/butterfly/transformation/TraxTransformer.java
  28. +230 −0 java/org/apache/butterfly/xml/AbstractXMLPipe.java
  29. +81 −0 java/org/apache/butterfly/xml/AbstractXMLProducer.java
  30. +58 −0 java/org/apache/butterfly/xml/EmbeddedXMLPipe.java
  31. +214 −0 java/org/apache/butterfly/xml/Parser.java
  32. +136 −0 java/org/apache/butterfly/xml/WhitespaceFilter.java
  33. +33 −0 java/org/apache/butterfly/xml/XMLConsumer.java
  34. +60 −0 java/org/apache/butterfly/xml/XMLException.java
  35. +26 −0 java/org/apache/butterfly/xml/XMLPipe.java
  36. +39 −0 java/org/apache/butterfly/xml/XMLProducer.java
  37. +141 −0 java/org/apache/butterfly/xml/dom/DOMBuilder.java
  38. +720 −0 java/org/apache/butterfly/xml/dom/DOMStreamer.java
  39. +60 −0 java/org/apache/butterfly/xml/xslt/TraxException.java
  40. +79 −0 java/org/apache/butterfly/xml/xslt/TraxTransformerFactory.java
  41. +77 −0 test/beans.xml
  42. +15 −0 test/log4j.properties
  43. +62 −0 test/org/apache/butterfly/components/pipeline/impl/GroovySitemapTestCase.java
  44. +13 −0 test/org/apache/butterfly/components/pipeline/impl/MyPipeline.groovy
  45. +398 −0 test/org/apache/butterfly/components/pipeline/impl/NonCachingProcessingPipelineTestCase.java
  46. +34 −0 test/org/apache/butterfly/components/pipeline/impl/Pipeline.groovy
  47. +56 −0 test/org/apache/butterfly/generation/FileGeneratorTestCase.java
  48. +58 −0 test/org/apache/butterfly/serialization/XMLSerializerTestCase.java
  49. +49 −0 test/org/apache/butterfly/source/SourceResolverTestCase.java
  50. +52 −0 test/org/apache/butterfly/test/SitemapComponentTestCase.java
  51. +61 −0 test/org/apache/butterfly/transformation/TraxTransformerTestCase.java
55 java/org/apache/butterfly/components/pipeline/InvalidPipelineException.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.components.pipeline;
+
+
+/**
+ * This exception is thrown whenever you try to assemble an invalid pipeline.
+ *
+ * @version CVS $Id: InvalidPipelineException.java,v 1.1 2004/07/24 20:21:33 ugo Exp $
+ */
+public class InvalidPipelineException extends PipelineException {
+
+ /**
+ *
+ */
+ public InvalidPipelineException() {
+ super();
+ }
+
+ /**
+ * @param arg0
+ */
+ public InvalidPipelineException(String arg0) {
+ super(arg0);
+ }
+
+ /**
+ * @param arg0
+ * @param arg1
+ */
+ public InvalidPipelineException(String arg0, Throwable arg1) {
+ super(arg0, arg1);
+ }
+
+ /**
+ * @param arg0
+ */
+ public InvalidPipelineException(Throwable arg0) {
+ super(arg0);
+ }
+
+}
55 java/org/apache/butterfly/components/pipeline/PipelineException.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.components.pipeline;
+
+
+/**
+ * Base class for all exceptions thrown during pipeline processing.
+ *
+ * @version CVS $Id: PipelineException.java,v 1.1 2004/07/24 20:21:33 ugo Exp $
+ */
+public abstract class PipelineException extends RuntimeException {
+
+ /**
+ *
+ */
+ public PipelineException() {
+ super();
+ }
+
+ /**
+ * @param arg0
+ */
+ public PipelineException(String arg0) {
+ super(arg0);
+ }
+
+ /**
+ * @param arg0
+ * @param arg1
+ */
+ public PipelineException(String arg0, Throwable arg1) {
+ super(arg0, arg1);
+ }
+
+ /**
+ * @param arg0
+ */
+ public PipelineException(Throwable arg0) {
+ super(arg0);
+ }
+
+}
55 java/org/apache/butterfly/components/pipeline/PipelineProcessingException.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.components.pipeline;
+
+
+/**
+ * This exception can be thrown when executing a pipeline.
+ *
+ * @version CVS $Id: PipelineProcessingException.java,v 1.1 2004/07/25 21:55:20 ugo Exp $
+ */
+public class PipelineProcessingException extends PipelineException {
+
+ /**
+ *
+ */
+ public PipelineProcessingException() {
+ super();
+ }
+
+ /**
+ * @param arg0
+ */
+ public PipelineProcessingException(String arg0) {
+ super(arg0);
+ }
+
+ /**
+ * @param arg0
+ * @param arg1
+ */
+ public PipelineProcessingException(String arg0, Throwable arg1) {
+ super(arg0, arg1);
+ }
+
+ /**
+ * @param arg0
+ */
+ public PipelineProcessingException(Throwable arg0) {
+ super(arg0);
+ }
+
+}
132 java/org/apache/butterfly/components/pipeline/ProcessingPipeline.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.components.pipeline;
+
+import org.apache.butterfly.environment.Environment;
+import org.apache.butterfly.generation.Generator;
+import org.apache.butterfly.reading.Reader;
+import org.apache.butterfly.serialization.Serializer;
+import org.apache.butterfly.source.SourceValidity;
+import org.apache.butterfly.transformation.Transformer;
+import org.apache.butterfly.xml.XMLConsumer;
+
+
+/**
+ * A <code>ProcessingPipeline<code> produces the response for a given request.
+ * It is assembled according to the commands in the sitemap and can either
+ * <ul>
+ * <li>collect a <code>Reader</code> and let it produce a character stream</li>
+ * <li>or connect a <code>Generator</code> with zero or more
+ * <code>Transformer</code>s and a <code>Serializer</code> and let them
+ * produce the byte stream. This pipeline uses SAX events for
+ * communication.
+ * </li>
+ * </ul>
+ *
+ * @version CVS $Id: ProcessingPipeline.java,v 1.2 2004/07/24 20:21:33 ugo Exp $
+ */
+public interface ProcessingPipeline {
+
+ /**
+ * Setup this component
+ */
+ // void setup(Parameters params);
+
+ /**
+ * Set the generator that will be used as the initial step in the pipeline.
+ * The generator role is given : the actual <code>Generator</code> is fetched
+ * from the latest <code>ComponentManager</code> given by <code>compose()</code>
+ * or <code>recompose()</code>.
+ *
+ * @param role the generator role in the component manager.
+ * @param source the source where to produce XML from, or <code>null</code> if no
+ * source is given.
+ * @param param the parameters for the generator.
+ * @throws ProcessingException if the generator couldn't be obtained.
+ */
+ void setGenerator(Generator generator);
+
+ /**
+ * Get the generator - used for content aggregation
+ */
+ Generator getGenerator();
+
+ /**
+ * Informs pipeline we have come across a branch point
+ */
+ void informBranchPoint();
+
+ /**
+ * Add a transformer at the end of the pipeline.
+ * The transformer role is given : the actual <code>Transformer</code> is fetched
+ * from the latest <code>ComponentManager</code> given by <code>compose()</code>
+ * or <code>recompose()</code>.
+ *
+ * @param role the transformer role in the component manager.
+ * @param source the source used to setup the transformer (e.g. XSL file), or
+ * <code>null</code> if no source is given.
+ * @param param the parameters for the transfomer.
+ * @throws ProcessingException if the generator couldn't be obtained.
+ */
+ void addTransformer(Transformer transformer);
+
+ /**
+ * Set the serializer for this pipeline
+ * @param mimeType Can be null
+ */
+ void setSerializer(Serializer serializer);
+
+ /**
+ * Set the reader for this pipeline
+ * @param mimeType Can be null
+ */
+ void setReader(Reader reader);
+
+ /**
+ * Process the given <code>Environment</code>, producing the output.
+ */
+ boolean process(Environment environment);
+
+ /**
+ * Prepare an internal processing
+ * @param environment The current environment.
+ * @throws ProcessingException
+ */
+ void prepareInternal(Environment environment);
+
+ /**
+ * Process the given <code>Environment</code>, but do not use the
+ * serializer. Instead the sax events are streamed to the XMLConsumer.
+ * Make sure to call {@link #prepareInternal(Environment)} beforehand.
+ */
+ boolean process(Environment environment, XMLConsumer consumer);
+
+ /**
+ * Return valid validity objects for the event pipeline
+ * If the "event pipeline" (= the complete pipeline without the
+ * serializer) is cacheable and valid, return all validity objects.
+ * Otherwise return <code>null</code>
+ */
+ SourceValidity getValidityForEventPipeline();
+
+ /**
+ * Return the key for the event pipeline
+ * If the "event pipeline" (= the complete pipeline without the
+ * serializer) is cacheable and valid, return a key.
+ * Otherwise return <code>null</code>
+ */
+ String getKeyForEventPipeline();
+}
265 java/org/apache/butterfly/components/pipeline/impl/NonCachingProcessingPipeline.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.components.pipeline.impl;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.butterfly.components.pipeline.InvalidPipelineException;
+import org.apache.butterfly.components.pipeline.PipelineProcessingException;
+import org.apache.butterfly.components.pipeline.ProcessingPipeline;
+import org.apache.butterfly.environment.Environment;
+import org.apache.butterfly.generation.Generator;
+import org.apache.butterfly.reading.Reader;
+import org.apache.butterfly.serialization.Serializer;
+import org.apache.butterfly.source.SourceValidity;
+import org.apache.butterfly.transformation.Transformer;
+import org.apache.butterfly.xml.XMLConsumer;
+import org.apache.butterfly.xml.XMLProducer;
+
+
+/**
+ * Implementation of the non-caching pipeline.
+ *
+ * TODO: change InvalidPipelineExceptions with PipelineExceptions
+ *
+ * @version CVS $Id: NonCachingProcessingPipeline.java,v 1.7 2004/07/25 21:55:20 ugo Exp $
+ */
+public class NonCachingProcessingPipeline implements ProcessingPipeline {
+
+ /** The generator */
+ protected Generator generator;
+
+ /** The reader */
+ protected Reader reader;
+
+ /** The transformers */
+ protected List transformers;
+
+ /** The serializer */
+ protected Serializer serializer;
+
+ /**
+ * This is the last component in the pipeline, either the serializer
+ * or a custom xmlconsumer for the cocoon: protocol etc.
+ */
+ protected XMLConsumer lastConsumer;
+
+ /** Output Buffer Size */
+ protected int outputBufferSize;
+
+ /**
+ * Build a new pipeline.
+ */
+ public NonCachingProcessingPipeline() {
+ transformers = new ArrayList();
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.components.pipeline.ProcessingPipeline#setGenerator(org.apache.butterfly.generation.Generator)
+ */
+ public void setGenerator(Generator generator) {
+ if (this.generator != null) {
+ throw new InvalidPipelineException("Generator already set. Cannot set generator.");
+ }
+ if (this.reader != null) {
+ throw new InvalidPipelineException("Reader already set. Cannot set generator.");
+ }
+ this.generator = generator;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.components.pipeline.ProcessingPipeline#setReader(org.apache.butterfly.reading.Reader)
+ */
+ public void setReader(Reader reader) {
+ if (this.generator != null) {
+ throw new InvalidPipelineException("Generator already set. Cannot set reader.");
+ }
+ if (this.reader != null) {
+ throw new InvalidPipelineException("Reader already set. Cannot set reader.");
+ }
+ this.reader = reader;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.components.pipeline.ProcessingPipeline#getGenerator()
+ */
+ public Generator getGenerator() {
+ return this.generator;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.components.pipeline.ProcessingPipeline#informBranchPoint()
+ */
+ public void informBranchPoint() {
+ // TODO Auto-generated method stub
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.components.pipeline.ProcessingPipeline#addTransformer(javax.xml.transform.Transformer)
+ */
+ public void addTransformer(Transformer transformer) {
+ if (this.generator == null) {
+ throw new InvalidPipelineException("Generator not yet set. Cannot add transformer.");
+ }
+ if (this.reader != null) {
+ throw new InvalidPipelineException("Reader already set. Cannot add transformer.");
+ }
+ transformers.add(transformer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.components.pipeline.ProcessingPipeline#setSerializer(org.apache.butterfly.xml.XMLPipe)
+ */
+ public void setSerializer(Serializer serializer) {
+ if (this.serializer != null) {
+ // Should normally not happen as adding a serializer starts pipeline processing
+ throw new InvalidPipelineException ("Serializer already set. Cannot set serializer.");
+ }
+ if (this.reader != null) {
+ // Should normally never happen as setting a reader starts pipeline processing
+ throw new InvalidPipelineException ("Reader already set. Cannot set serializer.");
+ }
+ if (this.generator == null) {
+ throw new InvalidPipelineException ("Must set a generator before setting serializer");
+ }
+ this.serializer = serializer;
+ this.lastConsumer = serializer;
+ }
+
+ /**
+ * Sanity check
+ * @return true if the pipeline is 'sane', false otherwise.
+ */
+ protected boolean checkPipeline() {
+ if (this.generator == null && this.reader == null) {
+ return false;
+ }
+ if (this.generator != null && this.serializer == null) {
+ return false;
+ }
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.components.pipeline.ProcessingPipeline#process()
+ */
+ public boolean process(Environment environment) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.components.pipeline.ProcessingPipeline#prepareInternal()
+ */
+ public void prepareInternal(Environment environment) {
+ // TODO Auto-generated method stub
+
+ }
+
+ /**
+ * Process the given <code>Environment</code>, but do not use the
+ * serializer. Instead the sax events are streamed to the XMLConsumer.
+ */
+ public boolean process(Environment environment, XMLConsumer consumer) {
+ this.lastConsumer = consumer;
+ if (this.reader != null) {
+ throw new InvalidPipelineException("Streaming of an internal pipeline is not possible with a reader.");
+ } else {
+ connectPipeline(environment);
+ return processXMLPipeline(environment);
+ }
+ }
+
+ /**
+ * Process the SAX event pipeline
+ */
+ protected boolean processXMLPipeline(Environment environment) {
+ if (this.serializer != this.lastConsumer) {
+ // internal processing
+ this.generator.generate();
+ } else {
+ if (this.serializer.shouldSetContentLength()) {
+ // set the output stream
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ this.serializer.setOutputStream(os);
+
+ // execute the pipeline:
+ this.generator.generate();
+ environment.setContentLength(os.size());
+ try {
+ os.writeTo(environment.getOutputStream(0));
+ } catch (IOException e) {
+ throw new PipelineProcessingException("Cannot write to output stream", e);
+ }
+ } else {
+ // set the output stream
+ this.serializer.setOutputStream(environment.getOutputStream(this.outputBufferSize));
+ // execute the pipeline:
+ this.generator.generate();
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Connect the next component
+ */
+ protected void connect(Environment environment,
+ XMLProducer producer,
+ XMLConsumer consumer) {
+ XMLProducer next = producer;
+ // Connect next component.
+ next.setConsumer(consumer);
+ }
+
+ /**
+ * Connect the XML pipeline.
+ */
+ protected void connectPipeline(Environment environment) {
+ XMLProducer prev = this.generator;
+
+ Iterator itt = this.transformers.iterator();
+ while (itt.hasNext()) {
+ Transformer next = (Transformer) itt.next();
+ connect(environment, prev, next);
+ prev = next;
+ }
+
+ // insert the serializer
+ connect(environment, prev, this.lastConsumer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.components.pipeline.ProcessingPipeline#getValidityForEventPipeline()
+ */
+ public SourceValidity getValidityForEventPipeline() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.components.pipeline.ProcessingPipeline#getKeyForEventPipeline()
+ */
+ public String getKeyForEventPipeline() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
191 java/org/apache/butterfly/environment/Environment.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.environment;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.Map;
+
+/**
+ * Base interface for an environment abstraction
+ *
+ * @version CVS $Id: Environment.java,v 1.2 2004/07/25 21:55:20 ugo Exp $
+ */
+public interface Environment {
+
+ /**
+ * Get the URI to process. The prefix is stripped off.
+ */
+ String getURI();
+
+ /**
+ * Get the prefix of the URI in progress.
+ */
+ String getURIPrefix();
+
+ /**
+ * Set the URI and the prefix to process.
+ */
+ void setURI(String prefix, String value);
+
+ /**
+ * Get the view to process
+ */
+ String getView();
+
+ /**
+ * Get the action to process
+ */
+ String getAction();
+
+ /**
+ * Redirect to the given URL
+ */
+ void redirect(String url, boolean global, boolean permanent)
+ throws IOException;
+
+ /**
+ * Set the content type of the generated resource
+ */
+ void setContentType(String mimeType);
+
+ /**
+ * Get the content type of the resource
+ */
+ String getContentType();
+
+ /**
+ * Set the length of the generated content
+ */
+ void setContentLength(int length);
+
+ /**
+ * Set the response status code
+ */
+ void setStatus(int statusCode);
+
+ /**
+ * Get the output stream where to write the generated resource.
+ * The returned stream is buffered by the environment. If the
+ * buffer size is -1 then the complete output is buffered.
+ * If the buffer size is 0, no buffering takes place.
+ */
+ OutputStream getOutputStream(int bufferSize);
+
+ /**
+ * Get the underlying object model
+ */
+ Map getObjectModel();
+
+ /**
+ * Check if the response has been modified since the same
+ * "resource" was requested.
+ * The caller has to test if it is really the same "resource"
+ * which is requested.
+ * @return true if the response is modified or if the
+ * environment is not able to test it
+ */
+ boolean isResponseModified(long lastModified);
+
+ /**
+ * Mark the response as not modified.
+ */
+ void setResponseIsNotModified();
+
+ /**
+ * Binds an object to this environment, using the name specified. This allows
+ * the pipeline assembly engine to store for its own use objects that souldn't
+ * be exposed to other components (generators, selectors, etc) and therefore
+ * cannot be put in the object model.
+ * <p>
+ * If an object of the same name is already bound, the object is replaced.
+ *
+ * @param name the name to which the object is bound
+ * @param value the object to be bound
+ */
+ void setAttribute(String name, Object value);
+
+ /**
+ * Returns the object bound with the specified name, or <code>null</code>
+ * if no object is bound under the name.
+ *
+ * @param name a string specifying the name of the object
+ * @return the object with the specified name
+ */
+ Object getAttribute(String name);
+
+ /**
+ * Removes the object bound with the specified name from
+ * this environment. If the environment does not have an object
+ * bound with the specified name, this method does nothing.
+ *
+ * @param name the name of the object to remove
+ */
+ void removeAttribute(String name);
+
+ /**
+ * Returns an <code>Enumeration</code> of <code>String</code> objects
+ * containing the names of all the objects bound to this environment.
+ *
+ * @return an <code>Enumeration</code> of <code>String</code>s.
+ */
+ Enumeration getAttributeNames();
+
+ /**
+ * Reset the response if possible. This allows error handlers to have
+ * a higher chance to produce clean output if the pipeline that raised
+ * the error has already output some data.
+ * If a buffered output stream is used, resetting is always successful.
+ *
+ * @return true if the response was successfully reset
+ */
+ boolean tryResetResponse() throws IOException;
+
+
+ /**
+ * Commit the response
+ */
+ void commitResponse() throws IOException;
+
+ /**
+ * Notify that the processing starts.
+ */
+ void startingProcessing();
+
+ /**
+ * Notify that the processing is finished
+ * This can be used to cleanup the environment object
+ */
+ void finishingProcessing();
+
+ /**
+ * Is this environment external ? An external environment is one that
+ * is created in response to an external request (http, commandline, etc.).
+ * Environments created by the "cocoon:" protocol aren't external.
+ *
+ * @return true if this environment is external
+ */
+ boolean isExternal();
+
+ /**
+ * Is this an internal redirect?
+ * An environment is on internal redirect if it is an internal request
+ * (via the cocoon: protocol) and used for a redirect.
+ */
+ boolean isInternalRedirect();
+}
+
64 java/org/apache/butterfly/generation/FileGenerator.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.generation;
+
+import org.apache.butterfly.source.Source;
+import org.apache.butterfly.source.SourceResolver;
+import org.apache.butterfly.xml.Parser;
+import org.apache.butterfly.xml.XMLConsumer;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * Description of FileGenerator.
+ *
+ * @version CVS $Id: FileGenerator.java,v 1.2 2004/07/25 21:55:20 ugo Exp $
+ */
+public class FileGenerator implements Generator {
+ private Source inputSource;
+ private SourceResolver sourceResolver;
+ private Parser parser;
+ protected static final Log logger = LogFactory.getLog(FileGenerator.class);
+
+ public void setParser(Parser parser) {
+ this.parser = parser;
+ }
+
+ public void setInputSource(String source) {
+ this.inputSource = sourceResolver.resolveURI(source);
+ }
+
+ /**
+ * @param sourceResolver The sourceResolver to set.
+ */
+ public void setSourceResolver(SourceResolver sourceResolver) {
+ this.sourceResolver = sourceResolver;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.generation.Generator#setContentHandler(org.xml.sax.ContentHandler)
+ */
+ public void setConsumer(XMLConsumer consumer) {
+ parser.setContentHandler(consumer);
+ }
+
+ public void generate() {
+ parser.parse(this.inputSource);
+ }
+
+}
29 java/org/apache/butterfly/generation/Generator.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.generation;
+
+import org.apache.butterfly.xml.XMLProducer;
+
+
+/**
+ * Description of Generator.
+ *
+ * @version CVS $Id: Generator.java,v 1.1 2004/07/23 08:47:20 ugo Exp $
+ */
+public interface Generator extends XMLProducer {
+ public void generate();
+}
41 java/org/apache/butterfly/reading/Reader.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.reading;
+
+import org.apache.butterfly.sitemap.SitemapOutputComponent;
+
+
+
+/**
+ * A reader can be used to generate binary output for a request.
+ *
+ * @version CVS $Id: Reader.java,v 1.1 2004/07/23 08:47:20 ugo Exp $
+ */
+public interface Reader extends SitemapOutputComponent {
+
+ String ROLE = Reader.class.getName();
+
+ /**
+ * Generate the response.
+ */
+ void generate();
+
+ /**
+ * @return the time the read source was last modified or 0 if it is not
+ * possible to detect
+ */
+ long getLastModified();
+}
29 java/org/apache/butterfly/serialization/Serializer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.serialization;
+
+import org.apache.butterfly.sitemap.SitemapOutputComponent;
+import org.apache.butterfly.xml.XMLConsumer;
+
+
+/**
+ * Description of Serializer.
+ *
+ * @version CVS $Id: Serializer.java,v 1.1 2004/07/24 20:21:33 ugo Exp $
+ */
+public interface Serializer extends XMLConsumer, SitemapOutputComponent {
+
+}
125 java/org/apache/butterfly/serialization/XMLSerializer.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.serialization;
+
+import java.io.OutputStream;
+import java.util.Properties;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+
+import org.apache.butterfly.xml.AbstractXMLPipe;
+import org.apache.butterfly.xml.xslt.TraxTransformerFactory;
+
+
+/**
+ * Description of XMLSerializer.
+ *
+ * @version CVS $Id: XMLSerializer.java,v 1.2 2004/07/24 20:21:33 ugo Exp $
+ */
+public class XMLSerializer extends AbstractXMLPipe implements Serializer {
+
+ protected OutputStream output;
+ protected TraxTransformerFactory transformerFactory;
+
+ /**
+ * The <code>Properties</code> used by this serializer.
+ */
+ protected Properties format = new Properties();
+
+ /**
+ *
+ */
+ public XMLSerializer() {
+ this.format.put(OutputKeys.METHOD, "xml");
+ }
+
+ /**
+ * @param transformerFactory The transformerFactory to set.
+ */
+ public void setTraxTransformerFactory(TraxTransformerFactory transformerFactory) {
+ this.transformerFactory = transformerFactory;
+ }
+
+ public void setCdataSectionElements(String cdataSectionElements) {
+ format.put(OutputKeys.CDATA_SECTION_ELEMENTS, cdataSectionElements);
+ }
+
+ public void setDoctypePublic(String dtPublic) {
+ format.put(OutputKeys.DOCTYPE_PUBLIC, dtPublic);
+ }
+
+ public void setDocTypeSystem(String dtSystem) {
+ format.put(OutputKeys.DOCTYPE_SYSTEM, dtSystem);
+ }
+
+ public void setEncoding(String encoding) {
+ format.put(OutputKeys.ENCODING, encoding);
+ }
+
+ public void setIndent(String indent) {
+ format.put(OutputKeys.INDENT, indent);
+ }
+
+ public void setMediaType(String mediaType) {
+ format.put(OutputKeys.MEDIA_TYPE, mediaType);
+ }
+
+ public void setMethod(String method) {
+ format.put(OutputKeys.METHOD, method);
+ }
+
+ public void setOmitXMLDeclaration(String omitXMLDeclaration) {
+ format.put(OutputKeys.OMIT_XML_DECLARATION, omitXMLDeclaration);
+ }
+
+ public void setStandAlone(String standAlone) {
+ format.put(OutputKeys.STANDALONE, standAlone);
+ }
+
+ public void setVersion(String version) {
+ format.put(OutputKeys.VERSION, version);
+ }
+
+ /**
+ * Set the {@link OutputStream} where the requested resource should
+ * be serialized.
+ */
+ public void setOutputStream(OutputStream output) {
+ this.output = output;
+ TransformerHandler handler = this.transformerFactory.getTransformerHandler();
+ handler.getTransformer().setOutputProperties(this.format);
+ handler.setResult(new StreamResult(this.output));
+ this.setContentHandler(handler);
+ this.setLexicalHandler(handler);
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.sitemap.SitemapOutputComponent#getMimeType()
+ */
+ public String getMimeType() {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.sitemap.SitemapOutputComponent#shouldSetContentLength()
+ */
+ public boolean shouldSetContentLength() {
+ return false;
+ }
+}
43 java/org/apache/butterfly/sitemap/SitemapOutputComponent.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.sitemap;
+
+import java.io.OutputStream;
+
+/**
+ * This interface marks a component as a sitemap component that produces
+ * a response, like a serializer or a reader.
+ *
+ * @version CVS $Id: SitemapOutputComponent.java,v 1.1 2004/07/23 08:47:20 ugo Exp $
+ */
+public interface SitemapOutputComponent {
+
+ /**
+ * Set the {@link OutputStream} where the requested resource should
+ * be serialized.
+ */
+ void setOutputStream(OutputStream out);
+
+ /**
+ * Get the mime-type of the output of this <code>Component</code>.
+ */
+ String getMimeType();
+
+ /**
+ * Test if the component wants to set the content length
+ */
+ boolean shouldSetContentLength();
+}
143 java/org/apache/butterfly/source/Source.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.source;
+
+import java.io.InputStream;
+
+
+/**
+ * This interface provides a simple interface for accessing a source of data.
+ * <p>
+ * When the <code>Source</code> object is no longer needed
+ * it must be released using the {@link SourceResolver}. This is very similar to
+ * looking up components from a <code>ServiceSelector</code>.
+ * In fact a source object can implement most lifecycle interfaces
+ * like Composable, Initializable, Disposable etc.
+ * <p>
+ * The data content can be constant or change over time.
+ * Using the {@link #getInputStream()} method you get always the up-to-date content.
+ * <p>
+ * If you want to track changes of the source object, this interface
+ * offers you some support for it by providing a SourceValidity object.
+ * <p>
+ * How does the caching work?
+ * The first time you get a Source object, you simply ask
+ * it for it's content via getInputStream() and then get the validity
+ * object by invoking getValidity. (Further calls to getValidity always
+ * return the same object! This is not updated!)
+ * The caching algorithm can now store this validity object together
+ * with the system identifier of the source.
+ * The next time, the caching algorithm wants to check if the cached
+ * content is still valid. It has a validity object already to check
+ * against.
+ * <p>
+ * If it is still the same Source than the first time, you
+ * have to call refresh() in order to discard the stored validity
+ * in the Source object. If it is a new Source object,
+ * calling refresh() should do no harm.
+ * After that an up-to-date validity object can retrieved by calling
+ * getValidity(). This can be used to test if the content is still valid
+ * as discribed in the source validity documentation.
+ * If the content is still valid, the cache knows what to do, if not,
+ * the new content can be get using getInputStream().
+ * So either after a call to getValidity() or the getInputStream the
+ * validity object must be the same until refresh is called!
+ *
+ * @version CVS $Id: Source.java,v 1.1 2004/07/23 08:47:20 ugo Exp $
+ */
+public interface Source {
+ /**
+ * Does this source exist ?
+ *
+ * @return true if the source exists
+ */
+ boolean exists();
+
+ /**
+ * Return an <code>InputStream</code> to read from the source.
+ * This is the data at the point of invocation of this method,
+ * so if this is Modifiable, you might get different content
+ * from two different invocations.
+ *
+ * @return the <code>InputStream</code> to read data from (never <code>null</code>).
+ */
+ InputStream getInputStream();
+
+ /**
+ * Get the absolute URI for this source.
+ *
+ * @return the source URI.
+ */
+ String getURI();
+
+ /**
+ * Return the URI scheme identifier, i.e. the part preceding the fist ':' in the URI
+ * (see <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>).
+ * <p>
+ * This scheme can be used to get the {@link SourceFactory} responsible for this object.
+ *
+ * @return the URI scheme.
+ */
+ String getScheme();
+
+ /**
+ * Get the Validity object. This can either wrap the last modification date or
+ * some expiry information or anything else describing this object's validity.
+ * <p>
+ * If it is currently not possible to calculate such an information,
+ * <code>null</code> is returned.
+ *
+ * @return the validity, or <code>null</code>.
+ */
+ SourceValidity getValidity();
+
+ /**
+ * Refresh the content of this object after the underlying data content has changed.
+ * <p>
+ * Some implementations may cache some values to speedup sucessive calls. Refreshing
+ * ensures you get the latest information.
+ */
+ void refresh();
+
+ /**
+ * Get the mime-type of the content described by this object.
+ * If the source is not able to determine the mime-type by itself
+ * this can be <code>null</code>.
+ *
+ * @return the source's mime-type or <code>null</code>.
+ */
+ String getMimeType();
+
+ /**
+ * Get the content length of this source's content or -1 if the length is
+ * unknown.
+ *
+ * @return the source's content length or -1.
+ */
+ long getContentLength();
+
+ /**
+ * Get the last modification date of this source. The date is
+ * measured in milliseconds since the epoch (00:00:00 GMT, January 1, 1970),
+ * and is <code>0</code> if it's unknown.
+ *
+ * @return the last modification date or <code>0</code>.
+ */
+ long getLastModified();
+
+
+}
42 java/org/apache/butterfly/source/SourceException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.source;
+
+
+/**
+ * Superclass of exceptions thrown by sources and source resolvers.
+ *
+ * @version CVS $Id: SourceException.java,v 1.2 2004/07/25 21:55:20 ugo Exp $
+ */
+public class SourceException extends RuntimeException {
+
+ public SourceException() {
+ super();
+ }
+
+ public SourceException(String arg0) {
+ super(arg0);
+ }
+
+ public SourceException(String arg0, Throwable arg1) {
+ super(arg0, arg1);
+ }
+
+ public SourceException(Throwable arg0) {
+ super(arg0);
+ }
+}
53 java/org/apache/butterfly/source/SourceFactory.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.source;
+
+import java.io.IOException;
+import java.util.Map;
+
+
+/**
+ * A source factory creates new source objects.
+ * <p>
+ * Source factories are used to extend the source resolving mechanism
+ * with new URI schemes. A new source factory is added in order to
+ * handle a specific prototol. The {@link SourceResolver} delegates
+ * the handling of a URI containing this new scheme to the factory,
+ * and the factory can create a corresponding {@link Source} object.
+ *
+ * @version CVS $Id: SourceFactory.java,v 1.1 2004/07/23 08:47:20 ugo Exp $
+ */
+public interface SourceFactory {
+
+ /**
+ * Get a {@link Source} object.
+ * The factory creates a new {@link Source} object that can be used
+ * by the application. However, when this source object is not needed
+ * anymore it has to be released again using the {@link #release(Source)}
+ * method. This is achieved by using {@link SourceResolver#release(Source)} which
+ * finds the appropriate <code>SourceFactory</code>.
+ *
+ * @param location The URI to resolve - this URI includes the scheme.
+ * @param parameters additionnal named parameters (optionnal and can be <code>null</code>)
+ * that drive the creation of the <code>Source</code> object. Each implementation
+ * must specify what parameters it accepts.
+ * @return the created source object.
+ *
+ * @throws IOException if the source couldn't be created for some reason.
+ */
+ Source getSource(String location, Map parameters);
+}
42 java/org/apache/butterfly/source/SourceNotFoundException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.source;
+
+
+/**
+ * Description of SourceException.
+ *
+ * @version CVS $Id: SourceNotFoundException.java,v 1.1 2004/07/23 08:47:20 ugo Exp $
+ */
+public class SourceNotFoundException extends RuntimeException {
+
+ public SourceNotFoundException() {
+ super();
+ }
+
+ public SourceNotFoundException(String arg0) {
+ super(arg0);
+ }
+
+ public SourceNotFoundException(String arg0, Throwable arg1) {
+ super(arg0, arg1);
+ }
+
+ public SourceNotFoundException(Throwable arg0) {
+ super(arg0);
+ }
+}
120 java/org/apache/butterfly/source/SourceResolver.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.source;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Map;
+
+
+/**
+ * Description of SourceResolver.
+ *
+ * @version CVS $Id: SourceResolver.java,v 1.2 2004/07/25 21:55:20 ugo Exp $
+ */
+public class SourceResolver {
+ private Map factories;
+ URL baseURL;
+
+ public SourceResolver() {
+ try {
+ // TODO: contextualize it
+ baseURL = new File(System.getProperty("user.dir")).toURL();
+ } catch (MalformedURLException e) {
+ throw new SourceException(e);
+ }
+ }
+
+ /**
+ * @param factories The factories to set.
+ */
+ public void setFactories(Map factories) {
+ this.factories = factories;
+ }
+
+ /**
+ * Get a <code>Source</code> object.
+ * @throws org.apache.excalibur.source.SourceNotFoundException if the source cannot be found
+ */
+ public Source resolveURI(String location) {
+ return this.resolveURI(location, null, null);
+ }
+
+ /**
+ * Get a <code>Source</code> object.
+ */
+ public Source resolveURI(String location,
+ String baseURI,
+ Map parameters)
+ {
+ if (null != baseURI && SourceUtil.indexOfSchemeColon(baseURI) == -1) {
+ throw new SourceException("BaseURI is not valid, it must contain a protocol: " + baseURI);
+ }
+
+ if( baseURI == null ) baseURI = baseURL.toExternalForm();
+
+ String systemID = location;
+ // special handling for windows file paths
+ if( location.length() > 1 && location.charAt( 1 ) == ':' )
+ systemID = "file:/" + location;
+ else if( location.length() > 2 && location.charAt(0) == '/' && location.charAt(2) == ':' )
+ systemID = "file:" + location;
+
+ // determine protocol (scheme): first try to get the one of the systemID, if that fails, take the one of the baseURI
+ String protocol;
+ int protocolPos = SourceUtil.indexOfSchemeColon(systemID);
+ if( protocolPos != -1 )
+ {
+ protocol = systemID.substring( 0, protocolPos );
+ }
+ else
+ {
+ protocolPos = SourceUtil.indexOfSchemeColon(baseURI);
+ if( protocolPos != -1 )
+ protocol = baseURI.substring( 0, protocolPos );
+ else
+ protocol = "*";
+ }
+
+ Source source = null;
+ // search for a SourceFactory implementing the protocol
+ SourceFactory factory = (SourceFactory) factories.get(protocol);
+ if (factory == null) {
+ factory = (SourceFactory) factories.get("*");
+ if (factory == null) {
+ throw new SourceException("Unable to select source factory for " + systemID);
+ }
+ systemID = absolutize(factory, baseURI, systemID);
+ return factory.getSource(systemID, parameters);
+ }
+ systemID = absolutize(factory, baseURI, systemID);
+ return factory.getSource(systemID, parameters);
+ }
+
+ /**
+ * Makes an absolute URI based on a baseURI and a relative URI.
+ */
+ private String absolutize( SourceFactory factory, String baseURI, String systemID )
+ {
+ if( factory instanceof URIAbsolutizer )
+ systemID = ((URIAbsolutizer) factory).absolutize(baseURI, systemID);
+ else
+ systemID = SourceUtil.absolutize(baseURI, systemID);
+ return systemID;
+ }
+}
473 java/org/apache/butterfly/source/SourceUtil.java
@@ -0,0 +1,473 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.source;
+
+import java.io.UnsupportedEncodingException;
+
+
+/**
+ * Utility class for source resolving.
+ *
+ * @version CVS $Id: SourceUtil.java,v 1.1 2004/07/23 08:47:20 ugo Exp $
+ */
+public class SourceUtil {
+
+ /**
+ * Get the position of the scheme-delimiting colon in an absolute URI, as specified
+ * by <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>, appendix A. This method is
+ * primarily useful for {@link Source} implementors that want to separate
+ * the scheme part from the specific part of an URI.
+ * <p>
+ * Use this method when you need both the scheme and the scheme-specific part of an URI,
+ * as calling successively {@link #getScheme(String)} and {@link #getSpecificPart(String)}
+ * will call this method twice, and as such won't be efficient.
+ *
+ * @param uri the URI
+ * @return int the scheme-delimiting colon, or <code>-1</code> if not found.
+ */
+ public static int indexOfSchemeColon(String uri)
+ {
+ // absoluteURI = scheme ":" ( hier_part | opaque_part )
+ //
+ // scheme = alpha *( alpha | digit | "+" | "-" | "." )
+ //
+ // alpha = lowalpha | upalpha
+ //
+ // lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" |
+ // "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" |
+ // "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
+ //
+ // upalpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" |
+ // "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" |
+ // "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
+ //
+ // digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
+ // "8" | "9"
+
+ // Must have at least one character followed by a colon
+ if (uri == null || uri.length() < 2)
+ {
+ return -1;
+ }
+
+ // Check that first character is alpha
+ // (lowercase first since it's the most common case)
+ char ch = uri.charAt(0);
+ if ( (ch < 'a' || ch > 'z') &&
+ (ch < 'A' || ch > 'Z') )
+ {
+ // Invalid first character
+ return -1;
+ }
+
+ int pos = uri.indexOf(':');
+ if (pos != -1)
+ {
+ // Check that every character before the colon is in the allowed range
+ // (the first one was tested above)
+ for (int i = 1; i < pos; i++)
+ {
+ ch = uri.charAt(i);
+ if ( (ch < 'a' || ch > 'z') &&
+ (ch < 'A' || ch > 'Z') &&
+ (ch < '0' || ch > '9') &&
+ ch != '+' && ch != '-' && ch != '.')
+ {
+ return -1;
+ }
+ }
+ }
+
+ return pos;
+ }
+
+ /**
+ * Get the scheme of an absolute URI.
+ *
+ * @param uri the absolute URI
+ * @return the URI scheme
+ */
+ public static String getScheme(String uri)
+ {
+ int pos = indexOfSchemeColon(uri);
+ return (pos == -1) ? null : uri.substring(0, pos);
+ }
+
+ /**
+ * Get the scheme-specific part of an absolute URI. Note that this includes everything
+ * after the separating colon, including the fragment, if any (RFC 2396 separates it
+ * from the scheme-specific part).
+ *
+ * @param uri the absolute URI
+ * @return the scheme-specific part of the URI
+ */
+ public static String getSpecificPart(String uri)
+ {
+ int pos = indexOfSchemeColon(uri);
+ return (pos == -1) ? null : uri.substring(pos+1);
+ }
+
+ /**
+ * Calls absolutize(url1, url2, false).
+ */
+ public static String absolutize(String url1, String url2)
+ {
+ return absolutize(url1, url2, false, true);
+ }
+
+ /**
+ * Calls absolutize(url1, url2, false, true).
+ */
+ public static String absolutize(String url1, String url2, boolean treatAuthorityAsBelongingToPath)
+ {
+ return absolutize(url1, url2, treatAuthorityAsBelongingToPath, true);
+ }
+
+ /**
+ * Applies a location to a baseURI. This is done as described in RFC 2396 section 5.2.
+ *
+ * @param url1 the baseURI
+ * @param url2 the location
+ * @param treatAuthorityAsBelongingToPath considers the authority to belong to the path. These
+ * special kind of URIs are used in the Apache Cocoon project.
+ * @param normalizePath should the path be normalized, i.e. remove ../ and /./ etc.
+ */
+ public static String absolutize(String url1, String url2, boolean treatAuthorityAsBelongingToPath, boolean normalizePath)
+ {
+ if (url1 == null)
+ return url2;
+
+ // If the URL contains a scheme (and thus is already absolute), don't do any further work
+ if (getScheme(url2) != null)
+ return url2;
+
+ // parse the urls into parts
+ // if the second url contains a scheme, it is not relative so return it right away (part 3 of the algorithm)
+ String[] url1Parts = parseUrl(url1);
+ String[] url2Parts = parseUrl(url2);
+
+ if (treatAuthorityAsBelongingToPath)
+ return absolutizeWithoutAuthority(url1Parts, url2Parts);
+
+ // check if it is a reference to the current document (part 2 of the algorithm)
+ if (url2Parts[PATH].equals("") && url2Parts[QUERY] == null && url2Parts[AUTHORITY] == null)
+ return makeUrl(url1Parts[SCHEME], url1Parts[AUTHORITY], url1Parts[PATH], url1Parts[QUERY], url2Parts[FRAGMENT]);
+
+ // it is a network reference (part 4 of the algorithm)
+ if (url2Parts[AUTHORITY] != null)
+ return makeUrl(url1Parts[SCHEME], url2Parts[AUTHORITY], url2Parts[PATH], url2Parts[QUERY], url2Parts[QUERY]);
+
+ String url1Path = url1Parts[PATH];
+ String url2Path = url2Parts[PATH];
+
+ // if the path starts with a slash (part 5 of the algorithm)
+ if (url2Path != null && url2Path.length() > 0 && url2Path.charAt(0) == '/')
+ return makeUrl(url1Parts[SCHEME], url1Parts[AUTHORITY], url2Parts[PATH], url2Parts[QUERY], url2Parts[QUERY]);
+
+ // combine the 2 paths
+ String path = stripLastSegment(url1Path);
+ path = path + (path.endsWith("/") ? "" : "/") + url2Path;
+ if (normalizePath)
+ path = normalize(path);
+
+ return makeUrl(url1Parts[SCHEME], url1Parts[AUTHORITY], path, url2Parts[QUERY], url2Parts[FRAGMENT]);
+ }
+
+ /**
+ * Absolutizes URIs whereby the authority part is considered to be a part of the path.
+ * This special kind of URIs is used in the Apache Cocoon project for the cocoon and context protocols.
+ * This method is internally used by {@link #absolutize}.
+ */
+ private static String absolutizeWithoutAuthority(String[] url1Parts, String[] url2Parts)
+ {
+ String authority1 = url1Parts[AUTHORITY];
+ String authority2 = url2Parts[AUTHORITY];
+
+ String path1 = url1Parts[PATH];
+ String path2 = url2Parts[PATH];
+
+ if (authority1 != null)
+ path1 = "//" + authority1 + path1;
+ if (authority2 != null)
+ path2 = "//" + authority2 + path2;
+
+ String path = stripLastSegment(path1);
+ path = path + (path.endsWith("/") ? "" : "/") + path2;
+ path = normalize(path);
+
+ String scheme = url1Parts[SCHEME];
+ return scheme + ":" + path;
+ }
+
+ private static String stripLastSegment(String path)
+ {
+ int i = path.lastIndexOf('/');
+ if(i > -1)
+ return path.substring(0, i + 1);
+ return path;
+ }
+
+ /**
+ * Removes things like &lt;segment&gt;/../ or ./, as described in RFC 2396 in
+ * step 6 of section 5.2.
+ */
+ private static String normalize(String path)
+ {
+ // replace all /./ with /
+ int i = path.indexOf("/./");
+ while (i > -1)
+ {
+ path = path.substring(0, i + 1) + path.substring(i + 3);
+ i = path.indexOf("/./");
+ }
+
+ if (path.endsWith("/."))
+ path = path.substring(0, path.length() - 1);
+
+ int f = path.indexOf("/../");
+ while (f > 0)
+ {
+ int sb = path.lastIndexOf("/", f - 1);
+ if (sb > - 1)
+ path = path.substring(0, sb + 1) + (path.length() >= f + 4 ? path.substring(f + 4) : "");
+ f = path.indexOf("/../");
+ }
+
+ if (path.length() > 3 && path.endsWith("/.."))
+ {
+ int sb = path.lastIndexOf("/", path.length() - 4);
+ String segment = path.substring(sb, path.length() - 3);
+ if (!segment.equals(".."))
+ {
+ path = path.substring(0, sb + 1);
+ }
+ }
+
+ return path;
+ }
+
+ /**
+ * Assembles an URL from the given URL parts, each of these parts can be null.
+ * Used internally by {@link #absolutize}.
+ */
+ private static String makeUrl(String scheme, String authority, String path, String query, String fragment)
+ {
+ StringBuffer url = new StringBuffer();
+ if (scheme != null)
+ url.append(scheme).append(':');
+
+ if (authority != null)
+ url.append("//").append(authority);
+
+ if (path != null)
+ url.append(path);
+
+ if (query != null)
+ url.append('?').append(query);
+
+ if (fragment != null)
+ url.append('#').append(fragment);
+
+ return url.toString();
+ }
+
+ public static final int SCHEME = 0;
+ public static final int AUTHORITY = 1;
+ public static final int PATH = 2;
+ public static final int QUERY = 3;
+ public static final int FRAGMENT = 4;
+
+ /**
+ * Parses an URL into the following parts: scheme, authority, path, query and fragment identifier.
+ *
+ * <p>The parsing is designed to be robust in the sense that it will never fail, even when an invalid
+ * URL is given. The parser will simply look for the most important delimiter characters. Basically
+ * it does the same as what would be achieved using the following regular expression (from RFC 2396):
+ * <pre>
+ * ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
+ * 12 3 4 5 6 7 8 9
+ * </pre>
+ * but without actually using the regular expression.
+ *
+ * <p>The result is returned as a string array, use the constants SCHEME, AUTHORITY, PATH,
+ * QUERY and FRAGMENT_IDENTIFIER to access the different parts.
+ *
+ * <p>If a part is missing, its corresponding entry in the array will be null, except for the
+ * path, which will never be null.
+ */
+ public static String[] parseUrl(String url) {
+ char[] urlchars = url.toCharArray();
+
+ int pos = 0;
+
+ String scheme = null;
+ String authority = null;
+ String path = null;
+ String query = null;
+ String fragid = null;
+
+ // ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
+
+ // the scheme
+ boolean keepgoing = true;
+ while (keepgoing && pos < urlchars.length)
+ {
+ switch (urlchars[pos])
+ {
+ case ':':
+ if (pos >= 1)
+ {
+ scheme = new String(urlchars, 0, pos);
+ keepgoing = false;
+ pos++;
+ break;
+ }
+ case '/':
+ case '?':
+ case '#':
+ keepgoing = false;
+ break;
+ default:
+ pos++;
+ }
+ }
+
+ if (scheme == null)
+ pos = 0;
+
+ // the authority
+ if (pos + 1 < urlchars.length && urlchars[pos] == '/' && urlchars[pos+1] == '/')
+ {
+ pos += 2;
+ int authorityBeginPos = pos;
+ keepgoing = true;
+ while (keepgoing && pos < urlchars.length)
+ {
+ switch (urlchars[pos])
+ {
+ case '/':
+ case '?':
+ case '#':
+ keepgoing = false;
+ break;
+ default:
+ pos++;
+ }
+ }
+ authority = new String(urlchars, authorityBeginPos, pos - authorityBeginPos);
+ }
+
+ // the path
+ int pathBeginPos = pos;
+ keepgoing = true;
+ while (keepgoing && pos < urlchars.length)
+ {
+ switch (urlchars[pos])
+ {
+ case '?':
+ case '#':
+ keepgoing = false;
+ break;
+ default:
+ pos++;
+ }
+ }
+ path = new String(urlchars, pathBeginPos, pos - pathBeginPos);
+
+ // the query
+ if (pos < urlchars.length && urlchars[pos] == '?')
+ {
+ pos++;
+ int queryBeginPos = pos;
+ keepgoing = true;
+ while (keepgoing && pos < urlchars.length)
+ {
+ switch (urlchars[pos])
+ {
+ case '#':
+ keepgoing = false;
+ break;
+ default:
+ pos++;
+ }
+ }
+ query = new String(urlchars, queryBeginPos, pos - queryBeginPos);
+ }
+
+ // the fragment identifier
+ pos++;
+ if (pos < urlchars.length)
+ fragid = new String(urlchars, pos, urlchars.length - pos);
+
+ return new String[] {scheme, authority, path, query, fragid};
+ }
+
+ /**
+ * Decode a path.
+ *
+ * <p>Interprets %XX (where XX is hexadecimal number) as UTF-8 encoded bytes.
+ * <p>The validity of the input path is not checked (i.e. characters that
+ * were not encoded will not be reported as errors).
+ * <p>This method differs from URLDecoder.decode in that it always uses UTF-8
+ * (while URLDecoder uses the platform default encoding, often ISO-8859-1),
+ * and doesn't translate + characters to spaces.
+ *
+ * @param path the path to decode
+ * @return the decoded path
+ */
+ public static String decodePath(String path) {
+ StringBuffer translatedPath = new StringBuffer(path.length());
+ byte[] encodedchars = new byte[path.length() / 3];
+ int i = 0;
+ int length = path.length();
+ int encodedcharsLength = 0;
+ while (i < length) {
+ if (path.charAt(i) == '%') {
+ // we must process all consecutive %-encoded characters in one go, because they represent
+ // an UTF-8 encoded string, and in UTF-8 one character can be encoded as multiple bytes
+ while (i < length && path.charAt(i) == '%') {
+ if (i + 2 < length) {
+ try {
+ byte x = (byte)Integer.parseInt(path.substring(i + 1, i + 3), 16);
+ encodedchars[encodedcharsLength] = x;
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Illegal hex characters in pattern %" + path.substring(i + 1, i + 3));
+ }
+ encodedcharsLength++;
+ i += 3;
+ } else {
+ throw new IllegalArgumentException("% character should be followed by 2 hexadecimal characters.");
+ }
+ }
+ try {
+ String translatedPart = new String(encodedchars, 0, encodedcharsLength, "UTF-8");
+ translatedPath.append(translatedPart);
+ } catch (UnsupportedEncodingException e) {
+ // the situation that UTF-8 is not supported is quite theoretical, so throw a runtime exception
+ throw new RuntimeException("Problem in decodePath: UTF-8 encoding not supported.");
+ }
+ encodedcharsLength = 0;
+ } else {
+ // a normal character
+ translatedPath.append(path.charAt(i));
+ i++;
+ }
+ }
+ return translatedPath.toString();
+ }
+
+}
75 java/org/apache/butterfly/source/SourceValidity.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.source;
+
+import java.io.Serializable;
+
+
+/**
+ * A <code>SourceValidity</code> object contains all information to check if a Source
+ * object is still valid.
+ * <p>
+ * There are two possibilities:
+ * <ul>
+ * <li>The validity object has all information to check by itself if it is valid
+ * (e.g. given an expires date).</li>
+ * <li>The validity object possibility needs another (newer) validity object to compare
+ * against (e.g. to test a last modification date).</li>
+ * </ul>
+ * To avoid testing what the actual implementation of the validity object supports,
+ * the invocation order is to first call {@link #isValid()} and only if this result
+ * is <code>0</code> (i.e. "don't know"), then to call {@link #isValid(SourceValidity)}.
+ * <p>
+ * Remember to call {@link #isValid(SourceValidity)} when {@link #isValid()} returned
+ * <code>0</code> !
+ *
+ * @version CVS $Id: SourceValidity.java,v 1.1 2004/07/23 08:47:20 ugo Exp $
+ */
+public interface SourceValidity extends Serializable {
+ final int VALID = +1;
+ final int INVALID = -1;
+ final int UNKNOWN = 0;
+
+ /**
+ * Check if the component is still valid. The possible results are :
+ * <ul>
+ * <li><code>-1</code>: invalid. The component isn't valid anymore.</li>
+ * <li><code>0</code>: don't know. This validity should be checked against a new
+ * validity object using {@link #isValid(SourceValidity)}.</li>
+ * <li><code>1</code>: valid. The component is still valid.</li>
+ * </ul>
+ */
+ int isValid();
+
+ /**
+ * Check if the component is still valid. This is only true if the incoming Validity
+ * is of the same type and has the "same" values.
+ * <p>
+ * The invocation order is that the isValid
+ * method of the old Validity object is called with the new one as a
+ * parameter.
+ * @return -1 is returned, if the validity object is not valid anymore
+ * +1 is returned, if the validity object is still valid
+ * 0 is returned, if the validity check could not be performed.
+ * In this case, the new validity object is not usable. Examples
+ * for this are: when the validity objects have different types,
+ * or when one validity object for any reason is not able to
+ * get the required information.
+ */
+ int isValid(SourceValidity newValidity);
+
+}
33 java/org/apache/butterfly/source/URIAbsolutizer.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.source;
+
+
+/**
+ * Implemented by a SourceFactory when it supports applying a relative URI
+ * to a base URI to form an absolute URI.
+ *
+ * <p>If a source factory does not implement this interface, the standard
+ * algorithm (as described in RFC 2396) will be used. This interface only
+ * needs to be implemented for source-types which have a different behaviour.
+ *
+ * @version CVS $Id: URIAbsolutizer.java,v 1.1 2004/07/23 08:47:20 ugo Exp $
+ */
+public interface URIAbsolutizer {
+
+ public String absolutize(String baseURI, String location);
+}
176 java/org/apache/butterfly/source/impl/FileSource.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.source.impl;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URLConnection;
+
+import org.apache.butterfly.source.Source;
+import org.apache.butterfly.source.SourceException;
+import org.apache.butterfly.source.SourceNotFoundException;
+import org.apache.butterfly.source.SourceUtil;
+import org.apache.butterfly.source.SourceValidity;
+import org.apache.butterfly.source.impl.validity.FileTimeStampValidity;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * Description of FileSource.
+ *
+ * @version CVS $Id: FileSource.java,v 1.1 2004/07/23 08:47:20 ugo Exp $
+ */
+public class FileSource implements Source {
+
+ /** The file */
+ private File file;
+
+ /** The scheme */
+ private String scheme;
+
+ /** The URI of this source */
+ private String uri;
+
+ /** The logger */
+ protected static final Log logger = LogFactory.getLog(FileSource.class);
+
+ public FileSource(String uri) {
+ int pos = SourceUtil.indexOfSchemeColon(uri);
+ if (pos < 0) {
+ throw new SourceException("Invalid URI : " + uri);
+ }
+ String scheme = uri.substring(0, pos);
+ String fileName = uri.substring(pos + 1);
+ fileName = SourceUtil.decodePath(fileName);
+ init(scheme, new File(fileName));
+ }
+
+ /**
+ * Builds a FileSource, given an URI scheme and a File.
+ *
+ * @param scheme
+ * @param file
+ * @throws SourceException
+ */
+ public FileSource(String scheme, File file) throws SourceException
+ {
+ init(scheme, file);
+ }
+
+ private void init(String scheme, File file) throws SourceException
+ {
+ this.scheme = scheme;
+
+ String uri;
+ try {
+ uri = file.toURL().toExternalForm();
+ } catch (MalformedURLException mue) {
+ // Can this really happen ?
+ throw new SourceException("Failed to get URL for file " + file, mue);
+ }
+ if (!uri.startsWith(scheme)) {
+ // Scheme is not "file:"
+ uri = scheme + ':' + uri.substring(uri.indexOf(':') + 1);
+ }
+ this.uri = uri;
+ this.file = file;
+ }
+
+ /**
+ * Get the associated file
+ */
+ public File getFile()
+ {
+ return this.file;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.source.Source#exists()
+ */
+ public boolean exists() {
+ return getFile().exists();
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.source.Source#getInputStream()
+ */
+ public InputStream getInputStream() {
+ try {
+ return new FileInputStream(this.file);
+ } catch (FileNotFoundException fnfe) {
+ throw new SourceNotFoundException(this.uri + " doesn't exist.", fnfe);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.source.Source#getURI()
+ */
+ public String getURI() {
+ return this.uri;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.source.Source#getScheme()
+ */
+ public String getScheme() {
+ return this.scheme;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.source.Source#getValidity()
+ */
+ public SourceValidity getValidity() {
+ if (this.file.exists()) {
+ return new FileTimeStampValidity(this.file);
+ } else {
+ return null;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.source.Source#refresh()
+ */
+ public void refresh() {
+ // Nothing to do
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.source.Source#getMimeType()
+ */
+ public String getMimeType() {
+ return URLConnection.getFileNameMap().getContentTypeFor(this.file.getName());
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.source.Source#getContentLength()
+ */
+ public long getContentLength() {
+ return this.file.length();
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.source.Source#getLastModified()
+ */
+ public long getLastModified() {
+ return this.file.lastModified();
+ }
+
+}
50 java/org/apache/butterfly/source/impl/FileSourceFactory.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.source.impl;
+
+import java.util.Map;
+
+import org.apache.butterfly.source.Source;
+import org.apache.butterfly.source.SourceFactory;
+import org.apache.butterfly.source.SourceUtil;
+import org.apache.butterfly.source.URIAbsolutizer;
+
+
+/**
+ * Description of FileSourceFactory.
+ *
+ * @version CVS $Id: FileSourceFactory.java,v 1.1 2004/07/23 08:47:20 ugo Exp $
+ */
+public class FileSourceFactory implements SourceFactory, URIAbsolutizer {
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.source.SourceFactory#getSource(java.lang.String, java.util.Map)
+ */
+ public Source getSource(String location, Map parameters) {
+ return new FileSource(location);
+ }
+
+ public String absolutize(String baseURI, String location)
+ {
+ // Call the absolutize utility method with false for the normalizePath argument.
+ // This avoids the removal of "../" from the path.
+ // This way, the "../" will be resolved by the operating system, which might
+ // do things differently e.g. in case of symbolic links.
+ return SourceUtil.absolutize(baseURI, location, false, false);
+ }
+
+}
109 java/org/apache/butterfly/source/impl/URLSource.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2004, Ugo Cei.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.butterfly.source.impl;
+
+import java.io.InputStream;
+
+import org.apache.butterfly.source.Source;
+import org.apache.butterfly.source.SourceValidity;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * Description of URLSource.
+ *
+ * @version CVS $Id: URLSource.java,v 1.1 2004/07/23 08:47:20 ugo Exp $
+ */
+public class URLSource implements Source {
+
+ /** The logger */
+ protected static final Log logger = LogFactory.getLog(URLSource.class);
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.source.Source#exists()
+ */
+ public boolean exists() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.source.Source#getInputStream()
+ */
+ public InputStream getInputStream() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.butterfly.source.Source#getURI()
+ */
+ public String getURI() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+