sylvain 2004/02/04 06:39:28
Modified: src/blocks/batik/java/org/apache/cocoon/serialization
SVGSerializer.java
Added: src/blocks/batik/java/org/apache/cocoon/components/url
SourceProtocolHandler.java
StreamJDKRegistryEntry.java
Log:
Add support for all cocoon protocols for <svg:image xlink:href="..."/>
Revision Changes Path
1.1
cocoon-2.1/src/blocks/batik/java/org/apache/cocoon/components/url/SourceProtocolHandler.java
Index: SourceProtocolHandler.java
===================================================================
/*
============================================================================
The Apache Software License, Version 1.1
============================================================================
Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
Redistribution and use in source and binary forms, with or without modifica-
tion, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. The end-user documentation included with the redistribution, if any, must
include the following acknowledgment: "This product includes software
developed by the Apache Software Foundation (http://www.apache.org/)."
Alternately, this acknowledgment may appear in the software itself, if
and wherever such third-party acknowledgments normally appear.
4. The names "Apache Cocoon" and "Apache Software Foundation" must not be
used to endorse or promote products derived from this software without
prior written permission. For written permission, please contact
[EMAIL PROTECTED]
5. Products derived from this software may not be called "Apache", nor may
"Apache" appear in their name, without prior written permission of the
Apache Software Foundation.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This software consists of voluntary contributions made by many individuals
on behalf of the Apache Software Foundation and was originally created by
Stefano Mazzocchi <[EMAIL PROTECTED]>. For more information on the Apache
Software Foundation, please see <http://www.apache.org/>.
*/
package org.apache.cocoon.components.url;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import org.apache.avalon.framework.CascadingRuntimeException;
import org.apache.batik.ext.awt.image.spi.ImageTagRegistry;
import org.apache.batik.util.AbstractParsedURLProtocolHandler;
import org.apache.batik.util.ParsedURL;
import org.apache.batik.util.ParsedURLData;
import org.apache.batik.util.ParsedURLProtocolHandler;
import org.apache.cocoon.CascadingIOException;
import org.apache.excalibur.source.Source;
import org.apache.excalibur.source.SourceResolver;
/**
* A Batik protocol handler that handles all Cocoon sources. This allows
* <svg:image xlink:href="..."/> to use any of the protocols handled by
Cocoon.
*
* @author <a href="http://www.apache.org/~sylvain/">Sylvain Wallez</a>
* @version CVS $Id: SourceProtocolHandler.java,v 1.1 2004/02/04 14:39:28
sylvain Exp $
*/
public class SourceProtocolHandler extends AbstractParsedURLProtocolHandler {
/** Thread-local source resolver */
protected static InheritableThreadLocal localResolver = new
InheritableThreadLocal();
/** Batik's original default handler */
protected static ParsedURLProtocolHandler defaultHandler;
/**
* Change the default handler used by Batik to resolve URLs to a handler
* based on <code>SourceResolver</code> and <code>SourceHandler</code>.
* <p>
* Note : Batik handlers are defined statically, and are thus shared by
* all its users. However, this shouldn't be a problem since different
* web applications live in different classloaders.
*
* @param manager the component manager used to get the
<code>SourceHandler</code>
* @param logger the logger for logging.
*/
static {
// Keep the default handler, if any
SourceProtocolHandler.defaultHandler = ParsedURL.getHandler(null);
// Set the default handler to our handler
ParsedURL.registerHandler(new SourceProtocolHandler(null));
// Add a new image registry entry to handle image streams
ImageTagRegistry.getRegistry().register(new StreamJDKRegistryEntry());
}
/**
* Set the resolver to be used within the current thread.
*/
public static void setup(SourceResolver resolver) {
localResolver.set(resolver);
}
/**
* Get the thread-local resolver.
*/
public static SourceResolver getSourceResolver()
{
SourceResolver resolver = (SourceResolver)localResolver.get();
return resolver;
}
//-------------------------------------------------------------------------
public SourceProtocolHandler(String protocol) {
super(protocol);
}
public ParsedURLData parseURL(ParsedURL baseURL, String urlStr) {
SourceResolver resolver = (SourceResolver)localResolver.get();
if (resolver == null) {
// Fall back to the previous default handler
return defaultHandler == null ? null :
defaultHandler.parseURL(baseURL, urlStr);
} else {
return new SourceParsedURLData(urlStr, resolver);
}
}
public ParsedURLData parseURL(String urlStr) {
SourceResolver resolver = (SourceResolver)localResolver.get();
if (resolver == null) {
return defaultHandler == null ? null :
defaultHandler.parseURL(urlStr);
} else {
return new SourceParsedURLData(urlStr, resolver);
}
}
/**
* Reimplementation of some methods of ParsedURLData since we cannot use
<code>java.net.URL</code>.
*/
static class SourceParsedURLData extends ParsedURLData {
public String url;
private Source source;
private SourceResolver resolver;
public SourceParsedURLData(String urlStr, SourceResolver resolver) {
this.url = urlStr;
this.resolver = resolver;
// ParsedURLData has some public members which seems to be
required to
// have a value. This sucks.
int pidx=0, idx;
idx = urlStr.indexOf(':');
if (idx != -1) {
// May have a protocol spec...
this.protocol = urlStr.substring(pidx, idx);
if (this.protocol.indexOf('/') == -1)
pidx = idx+1;
else {
// Got a slash in protocol probably means
// no protocol given, (host and port?)
this.protocol = null;
pidx = 0;
}
}
idx = urlStr.indexOf(',',pidx);
if (idx != -1) {
this.host = urlStr.substring(pidx, idx);
pidx = idx+1;
}
if (pidx != urlStr.length())
this.path = urlStr.substring(pidx);
// Now do the real job
// Setup source
try {
this.source = resolver.resolveURI(this.url);
} catch(Exception e) {
throw new CascadingRuntimeException("Cannot resolve " +
this.url, e);
}
// Get Mime-type
// First try the source itself
this.contentType = this.source.getMimeType();
if (this.contentType == null) {
// Guess it from the URL extension
if (url.endsWith(".gif")) {
this.contentType = "image/gif";
} else if (url.endsWith(".jpeg") || url.endsWith(".jpg")) {
this.contentType = "image/jpeg";
} else if (url.endsWith(".png")) {
this.contentType = "image/png";
}
}
}
public boolean complete() {
return (this.url != null);
}
public String getPortStr() {
String portStr = protocol+":";
if (host != null) portStr += host;
portStr += ",";
return portStr;
}
public String toString() {
return this.url;
}
/**
* Open a stream for the data. If the thread-local
<code>SourceResolver</code> exists,
* then use it, otherwise fall back to <code>SourceHandler</code>.
*/
protected InputStream openStreamInternal (String userAgent, Iterator
mimeTypes, Iterator encodingTypes)
throws IOException {
try {
return source.getInputStream();
} catch(Exception e) {
throw new CascadingIOException("Cannot open URL " + this.url,
e);
} finally {
this.resolver.release(this.source);
}
}
}
}
1.1
cocoon-2.1/src/blocks/batik/java/org/apache/cocoon/components/url/StreamJDKRegistryEntry.java
Index: StreamJDKRegistryEntry.java
===================================================================
/*
============================================================================
The Apache Software License, Version 1.1
============================================================================
Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
Redistribution and use in source and binary forms, with or without modifica-
tion, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. The end-user documentation included with the redistribution, if any, must
include the following acknowledgment: "This product includes software
developed by the Apache Software Foundation (http://www.apache.org/)."
Alternately, this acknowledgment may appear in the software itself, if
and wherever such third-party acknowledgments normally appear.
4. The names "Apache Cocoon" and "Apache Software Foundation" must not be
used to endorse or promote products derived from this software without
prior written permission. For written permission, please contact
[EMAIL PROTECTED]
5. Products derived from this software may not be called "Apache", nor may
"Apache" appear in their name, without prior written permission of the
Apache Software Foundation.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This software consists of voluntary contributions made by many individuals
on behalf of the Apache Software Foundation and was originally created by
Stefano Mazzocchi <[EMAIL PROTECTED]>. For more information on the Apache
Software Foundation, please see <http://www.apache.org/>.
*/
package org.apache.cocoon.components.url;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Label;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import org.apache.batik.ext.awt.image.GraphicsUtil;
import org.apache.batik.ext.awt.image.renderable.Filter;
import org.apache.batik.ext.awt.image.renderable.RedRable;
import org.apache.batik.ext.awt.image.spi.AbstractRegistryEntry;
import org.apache.batik.ext.awt.image.spi.MagicNumberRegistryEntry;
import org.apache.batik.ext.awt.image.spi.URLRegistryEntry;
import org.apache.batik.util.ParsedURL;
/**
* This Image tag registy entry is setup to wrap the core JDK Image stream
tools.
*
* @version CVS $Id: StreamJDKRegistryEntry.java,v 1.1 2004/02/04 14:39:28
sylvain Exp $
*/
public class StreamJDKRegistryEntry extends AbstractRegistryEntry
implements URLRegistryEntry {
/**
* The priority of this entry.
* This entry should in most cases be the last entry.
* but if one wishes one could set a priority higher and be called
* afterwords
*/
public final static float PRIORITY =
1000*MagicNumberRegistryEntry.PRIORITY;
public StreamJDKRegistryEntry() {
super ("Stream-JDK", PRIORITY, new String[0], new String []
{"image/gif"});
}
/**
* Check if the Stream references an image that can be handled by
* this format handler. The input stream passed in should be
* assumed to support mark and reset.
*
* If this method throws a StreamCorruptedException then the
* InputStream will be closed and a new one opened (if possible).
*
* This method should only throw a StreamCorruptedException if it
* is unable to restore the state of the InputStream
* (i.e. mark/reset fails basically).
*/
public boolean isCompatibleURL(ParsedURL purl) {
String contentType = purl.getContentType();
if (contentType == null)
return false;
Iterator iter = this.getMimeTypes().iterator();
while (iter.hasNext()) {
if (contentType.equals(iter.next())) {
return true;
}
}
return false;
}
/**
* Decode the URL into a RenderableImage
*
* @param purl The URLto decode
* @param needRawData If true the image returned should not have
* any default color correction the file may
* specify applied.
*/
public Filter handleURL(ParsedURL purl, boolean needRawData) {
// Read all bytes from the ParsedURL (too bad, there's no
Toolkit.createImage(InputStream))
InputStream is = null;
byte[] buffer = new byte[1024];
int len;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
is = purl.openStream();
while((len = is.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
} catch(IOException ioe) {
return null;
} finally {
try {
if (is != null) is.close();
} catch (Exception e) {}
}
buffer = bos.toByteArray();
Toolkit tk = Toolkit.getDefaultToolkit();
final Image img = tk.createImage(buffer);
if (img == null)
return null;
RenderedImage ri = loadImage(img);
if (ri == null)
return null;
return new RedRable(GraphicsUtil.wrap(ri));
}
// Stuff for Image Loading.
static Component mediaComponent = new Label();
static MediaTracker mediaTracker = new MediaTracker(mediaComponent);
static int id = 0;
public RenderedImage loadImage(Image img) {
// In some cases the image will be a
// BufferedImage (subclass of RenderedImage).
if (img instanceof RenderedImage)
return (RenderedImage)img;
// Setup the mediaTracker.
int myID;
synchronized (mediaTracker) {
myID = id++;
}
// Add our image to the media tracker and wait....
mediaTracker.addImage(img, myID);
while (true) {
try {
mediaTracker.waitForID(myID);
}
catch(InterruptedException ie) {
// Something woke us up but the image
// isn't done yet, so try again.
continue;
};
// All done!
break;
}
// Clean up our registraction
mediaTracker.removeImage(img, myID);
if ((img.getWidth(null) == -1)||
(img.getHeight(null) == -1))
return null;
// Build the image to .
BufferedImage bi = null;
bi = new BufferedImage(img.getWidth(null),
img.getHeight(null),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bi.createGraphics();
g2d.drawImage(img, 0, 0, null);
g2d.dispose();
return bi;
}
}
1.11 +35 -19
cocoon-2.1/src/blocks/batik/java/org/apache/cocoon/serialization/SVGSerializer.java
Index: SVGSerializer.java
===================================================================
RCS file:
/home/cvs/cocoon-2.1/src/blocks/batik/java/org/apache/cocoon/serialization/SVGSerializer.java,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -r1.10 -r1.11
--- SVGSerializer.java 20 Nov 2003 15:13:36 -0000 1.10
+++ SVGSerializer.java 4 Feb 2004 14:39:28 -0000 1.11
@@ -54,26 +54,25 @@
import java.io.OutputStream;
import org.apache.avalon.excalibur.pool.Poolable;
+import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
-import org.apache.avalon.framework.context.Context;
-import org.apache.avalon.framework.context.ContextException;
-import org.apache.avalon.framework.context.Contextualizable;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
import org.apache.batik.transcoder.Transcoder;
+import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.TranscodingHints;
-import org.apache.batik.transcoder.TranscoderException;
-import org.apache.batik.util.ParsedURL;
-import org.apache.cocoon.Constants;
import org.apache.cocoon.caching.CacheableProcessingComponent;
import org.apache.cocoon.components.transcoder.ExtendableTranscoderFactory;
import org.apache.cocoon.components.transcoder.TranscoderFactory;
-import org.apache.cocoon.components.url.ParsedContextURLProtocolHandler;
-import org.apache.cocoon.components.url.ParsedResourceURLProtocolHandler;
+import org.apache.cocoon.components.url.SourceProtocolHandler;
import org.apache.cocoon.util.ClassUtils;
import org.apache.cocoon.xml.dom.SVGBuilder;
+import org.apache.excalibur.source.SourceResolver;
import org.apache.excalibur.source.SourceValidity;
import org.apache.excalibur.source.impl.validity.NOPValidity;
import org.w3c.dom.Document;
@@ -87,17 +86,17 @@
* @version CVS $Id$
*/
public class SVGSerializer extends SVGBuilder
-implements Serializer, Configurable, Poolable, CacheableProcessingComponent,
Contextualizable {
+implements Serializer, Configurable, Poolable, CacheableProcessingComponent,
Serviceable, Disposable /*, Contextualizable*/ {
- /**
- * Get the context
- */
- public void contextualize(Context context) throws ContextException {
- ParsedContextURLProtocolHandler.setContext(
-
(org.apache.cocoon.environment.Context)context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT));
- ParsedURL.registerHandler(new ParsedContextURLProtocolHandler());
- ParsedURL.registerHandler(new ParsedResourceURLProtocolHandler());
- }
+// /**
+// * Get the context
+// */
+// public void contextualize(Context context) throws ContextException {
+// ParsedContextURLProtocolHandler.setContext(
+//
(org.apache.cocoon.environment.Context)context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT));
+// ParsedURL.registerHandler(new ParsedContextURLProtocolHandler());
+// ParsedURL.registerHandler(new ParsedResourceURLProtocolHandler());
+// }
/** The current <code>OutputStream</code>. */
private OutputStream output;
@@ -110,12 +109,28 @@
/** The Transcoder Factory to use */
TranscoderFactory factory =
ExtendableTranscoderFactory.getTranscoderFactoryImplementation();
+
+ private ServiceManager manager;
+
+ private SourceResolver resolver;
/**
* Set the <code>OutputStream</code> where the XML should be serialized.
*/
public void setOutputStream(OutputStream out) {
this.output = out;
+
+ // Give the source resolver to Batik
+ SourceProtocolHandler.setup(this.resolver);
+ }
+
+ public void service(ServiceManager manager) throws ServiceException {
+ this.manager = manager;
+ this.resolver =
(SourceResolver)this.manager.lookup(SourceResolver.ROLE);
+ }
+
+ public void dispose() {
+ this.manager.release(this.resolver);
}
/**
@@ -215,6 +230,7 @@
* Receive notification of a successfully completed DOM tree generation.
*/
public void notify(Document doc) throws SAXException {
+
try {
TranscoderInput transInput = new TranscoderInput(doc);