001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.lenya.modules.svg;
019:
020: import java.io.IOException;
021: import java.io.InputStream;
022: import java.util.Iterator;
023:
024: import org.apache.avalon.framework.activity.Startable;
025: import org.apache.avalon.framework.component.Component;
026: import org.apache.avalon.framework.logger.AbstractLogEnabled;
027: import org.apache.avalon.framework.parameters.ParameterException;
028: import org.apache.avalon.framework.parameters.Parameterizable;
029: import org.apache.avalon.framework.parameters.Parameters;
030: import org.apache.avalon.framework.service.ServiceException;
031: import org.apache.avalon.framework.service.ServiceManager;
032: import org.apache.avalon.framework.service.Serviceable;
033: import org.apache.avalon.framework.thread.ThreadSafe;
034: import org.apache.batik.util.ParsedURL;
035: import org.apache.batik.util.ParsedURLData;
036: import org.apache.batik.util.ParsedURLProtocolHandler;
037: import org.apache.cocoon.CascadingIOException;
038: import org.apache.excalibur.source.Source;
039: import org.apache.excalibur.source.SourceResolver;
040: import org.apache.lenya.util.Assert;
041:
042: /**
043: * Batik URL protocol handler for protocols which are handled by the SourceResolver.
044: */
045: public class ProtocolHandler extends AbstractLogEnabled implements
046: ParsedURLProtocolHandler, ThreadSafe, Startable, Component,
047: Serviceable, Parameterizable {
048:
049: protected static final String PARAM_PROTOCOL = "protocol";
050:
051: public static final String ROLE = ProtocolHandler.class.getName();
052:
053: private SourceResolver resolver;
054: private ServiceManager manager;
055:
056: private String protocol;
057:
058: public ParsedURLData parseURL(String urlStr) {
059: if (this .resolver == null) {
060: throw new IllegalStateException(
061: "Please call setResolver() first!");
062: }
063: return new ParsedUrlData(getProtocolHandled(), this .resolver,
064: urlStr);
065: }
066:
067: public ParsedURLData parseURL(ParsedURL basepurl, String urlStr) {
068: return parseURL(urlStr);
069: }
070:
071: static class ParsedUrlData extends ParsedURLData {
072:
073: private Source source;
074: private SourceResolver resolver;
075: private String url;
076: private long lastModified;
077:
078: public ParsedUrlData(String protocol, SourceResolver resolver,
079: String url) {
080: this .url = url;
081: String prefix = protocol + ":";
082: Assert.isTrue("valid URL", url.length() > prefix.length());
083: this .path = url.substring(prefix.length());
084: this .protocol = protocol;
085: this .resolver = resolver;
086: try {
087: this .source = resolver.resolveURI(url);
088: this .lastModified = this .source.getLastModified();
089: this .contentType = this .source.getMimeType();
090: } catch (Exception e) {
091: throw new RuntimeException(e);
092: }
093: }
094:
095: /**
096: * Open a stream for the data.
097: */
098: protected InputStream openStreamInternal(String userAgent,
099: Iterator mimeTypes, Iterator encodingTypes)
100: throws IOException {
101: try {
102: return this .source.getInputStream();
103: } catch (Exception e) {
104: throw new CascadingIOException("Cannot open URL "
105: + this .url, e);
106: } finally {
107: this .resolver.release(this .source);
108: }
109: }
110:
111: // Batik uses toString for caching.
112: // Use URL and last modified time for proper caching of source images.
113: public String toString() {
114: return url + source.getLastModified();
115: }
116:
117: /**
118: * Object.equals
119: */
120: public boolean equals(Object obj) {
121: if (obj == null) {
122: return false;
123: }
124: if (!(obj instanceof ParsedUrlData)) {
125: return false;
126: }
127: // super.equals() doesn't take lastModified into account
128: ParsedUrlData pud = (ParsedUrlData) obj;
129: if (pud.url.equals(this .url)
130: && pud.lastModified == this .lastModified) {
131: return true;
132: }
133: return false;
134: }
135:
136: /**
137: * Returns hashCode for object
138: */
139: public int hashCode() {
140: return (url + lastModified).hashCode();
141: }
142: }
143:
144: public void start() throws Exception {
145: this .resolver = (SourceResolver) this .manager
146: .lookup(SourceResolver.ROLE);
147: ParsedURL.registerHandler(this );
148: }
149:
150: public void stop() throws Exception {
151: }
152:
153: public void service(ServiceManager manager) throws ServiceException {
154: this .manager = manager;
155: }
156:
157: public String getProtocolHandled() {
158: return this .protocol;
159: }
160:
161: public void parameterize(Parameters params)
162: throws ParameterException {
163: this.protocol = params.getParameter(PARAM_PROTOCOL);
164: }
165:
166: }
|