Return-Path: Delivered-To: apmail-cocoon-cvs-archive@www.apache.org Received: (qmail 22520 invoked from network); 21 Mar 2008 13:57:19 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 21 Mar 2008 13:57:19 -0000 Received: (qmail 48461 invoked by uid 500); 21 Mar 2008 13:57:17 -0000 Delivered-To: apmail-cocoon-cvs-archive@cocoon.apache.org Received: (qmail 48377 invoked by uid 500); 21 Mar 2008 13:57:17 -0000 Mailing-List: contact cvs-help@cocoon.apache.org; run by ezmlm Precedence: bulk Reply-To: dev@cocoon.apache.org list-help: list-unsubscribe: List-Post: List-Id: Delivered-To: mailing list cvs@cocoon.apache.org Received: (qmail 48326 invoked by uid 99); 21 Mar 2008 13:57:17 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 21 Mar 2008 06:57:17 -0700 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 21 Mar 2008 13:56:20 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id DE5801A984F; Fri, 21 Mar 2008 06:56:36 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r639645 [5/7] - in /cocoon/whiteboard/corona/trunk: ./ corona-core/ corona-core/src/ corona-core/src/main/ corona-core/src/main/java/ corona-core/src/main/java/org/ corona-core/src/main/java/org/apache/ corona-core/src/main/java/org/apache/... Date: Fri, 21 Mar 2008 13:55:20 -0000 To: cvs@cocoon.apache.org From: reinhard@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20080321135636.DE5801A984F@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Added: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceParameters.java URL: http://svn.apache.org/viewvc/cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceParameters.java?rev=639645&view=auto ============================================================================== --- cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceParameters.java (added) +++ cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceParameters.java Fri Mar 21 06:54:32 2008 @@ -0,0 +1,477 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.excalibur.source; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.StringTokenizer; + +/** + * This class holds parameters for a Source object. + * It differs from the usual Parameters object because it can hold + * more than one value for a parameter, as is the case for HTTP + * request parameters. + *

+ * Only particular kinds of Source implementations, such as + * {@link org.apache.excalibur.source.factories.URLSource} support this kind of + * parameters, passed as the {@link SourceResolver#URI_PARAMETERS} entry + * in the parameters argument of + * {@link SourceResolver#resolveURI(String, String, Map)}. + * + * @author Avalon Development Team + * @version $Id$ + */ +public final class SourceParameters + implements Serializable, Cloneable +{ + /** The parameter names are the keys and the value is a List object */ + private Map names = new HashMap( 5 ); + + /** + * Decode the string + */ + private String parseName( String s ) + { + StringBuffer sb = new StringBuffer(); + for( int i = 0; i < s.length(); i++ ) + { + char c = s.charAt( i ); + switch( c ) + { + case '+': + sb.append( ' ' ); + break; + case '%': + try + { + sb.append( (char)Integer.parseInt( s.substring( i + 1, i + 3 ), + 16 ) ); + i += 2; + } + catch( NumberFormatException e ) + { + throw new IllegalArgumentException(); + } + catch( StringIndexOutOfBoundsException e ) + { + String rest = s.substring( i ); + sb.append( rest ); + if( rest.length() == 2 ) + i++; + } + + break; + default: + sb.append( c ); + break; + } + } + return sb.toString(); + } + + /** + * Standard Constructor creating an empty parameters object + */ + public SourceParameters() + { + } + + /** + * Construct a new object from a queryString + */ + public SourceParameters( String queryString ) + { + if( queryString != null ) + { + StringTokenizer st = new StringTokenizer( queryString, "&" ); + while( st.hasMoreTokens() ) + { + String pair = st.nextToken(); + int pos = pair.indexOf( '=' ); + if( pos != -1 ) + { + setParameter( parseName( pair.substring( 0, pos ) ), + parseName( pair.substring( pos + 1, pair.length() ) ) ); + } + } + } + } + + /** + * Add a parameter. + * The parameter is added with the given value. + * @param name The name of the parameter. + * @param value The value of the parameter. + */ + public void setParameter( String name, String value ) + { + ArrayList list; + if( names.containsKey( name ) == true ) + { + list = (ArrayList)names.get( name ); + } + else + { + list = new ArrayList( 3 ); + names.put( name, list ); + } + list.add( value ); + } + + /** + * Get the value of a parameter. + * @param name The name of the parameter. + * @return The value of the first parameter with the name + * or null + */ + public String getParameter( String name ) + { + if( names.containsKey( name ) == true ) + { + return (String)( (ArrayList)names.get( name ) ).get( 0 ); + } + return null; + } + + /** + * Get the value of a parameter. + * @param name The name of the parameter. + * @param defaultValue The default value if the parameter does not exist. + * @return The value of the first parameter with the name + * or defaultValue + */ + public String getParameter( String name, String defaultValue ) + { + if( names.containsKey( name ) == true ) + { + return (String)( (ArrayList)names.get( name ) ).get( 0 ); + } + return defaultValue; + } + + /** + * Get the integer value of a parameter. + * @param name The name of the parameter. + * @param defaultValue The default value if the parameter does not exist. + * @return The value of the first parameter with the name + * or defaultValue + */ + public int getParameterAsInteger( String name, int defaultValue ) + { + if( names.containsKey( name ) == true ) + { + return new Integer( (String)( (ArrayList)names.get( name ) ).get( 0 ) ).intValue(); + } + return defaultValue; + } + + /** + * Get the boolean value of a parameter. + * @param name The name of the parameter. + * @param defaultValue The default value if the parameter does not exist. + * @return The value of the first parameter with the name + * or defaultValue + */ + public boolean getParameterAsBoolean( String name, boolean defaultValue ) + { + if( names.containsKey( name ) == true ) + { + return new Boolean( (String)( (ArrayList)names.get( name ) ).get( 0 ) ).booleanValue(); + } + return defaultValue; + } + + /** + * Test if a value for this parameter exists. + * @param name The name of the parameter. + * @return true if a value exists, otherwise false + */ + public boolean containsParameter( String name ) + { + return names.containsKey( name ); + } + + /** + * Get all values of a parameter. + * @param name The name of the parameter. + * @return Iterator for the (String) values or null if the parameter + * is not defined. + */ + public Iterator getParameterValues( String name ) + { + if( names.containsKey( name ) == true ) + { + ArrayList list = (ArrayList)names.get( name ); + return list.iterator(); + } + return null; + } + + /** + * Get all values of a parameter. + * @param name The name of the parameter. + * @return An Array for the (String) values or null. + */ + public String[] getParameterValuesAsArray( String name ) + { + if( names.containsKey( name ) == true ) + { + ArrayList list = (ArrayList)names.get( name ); + String[] values = new String[list.size()]; + for(int i=0;inull is returned. + */ + public String getQueryString() + { + StringBuffer result = new StringBuffer(); + Iterator iter = this.names.keySet().iterator(); + Iterator listIterator; + String key; + String value; + boolean first = true; + while( iter.hasNext() == true ) + { + key = (String)iter.next(); + listIterator = ( (ArrayList)names.get( key ) ).iterator(); + while( listIterator.hasNext() == true ) + { + if( first == false ) result.append( '&' ); + value = (String)listIterator.next(); + result.append( key ).append( '=' ).append( value ); + first = false; + } + } + return ( result.length() == 0 ? null : result.toString() ); + } + + /** + * Build a query string and encode each parameter value. + * The query string can e.g. be used for http connections. + * @return A query string which contains for each parameter/value pair + * a part, like "parameter=value" separated by "&". + * If no parameter is defined null is returned. + */ + public String getEncodedQueryString() + { + StringBuffer result = new StringBuffer(); + Iterator iter = this.names.keySet().iterator(); + Iterator listIterator; + String key; + String value; + boolean first = true; + while( iter.hasNext() == true ) + { + key = (String)iter.next(); + listIterator = ( (ArrayList)names.get( key ) ).iterator(); + while( listIterator.hasNext() == true ) + { + if( first == false ) result.append( '&' ); + value = (String)listIterator.next(); + result.append( key ).append( '=' ).append( SourceUtil.encode( value ) ); + first = false; + } + } + return ( result.length() == 0 ? null : result.toString() ); + } + + /** + * Add all parameters from the incoming parameters object. + */ + public void add( SourceParameters parameters ) + { + if( null != parameters ) + { + Iterator names = parameters.getParameterNames(); + Iterator values; + String name; + String value; + while( names.hasNext() == true ) + { + name = (String)names.next(); + values = parameters.getParameterValues( name ); + while( values.hasNext() == true ) + { + value = (String)values.next(); + this.setParameter( name, value ); + } + } + } + } + + /** + * Overriding toString + */ + public String toString() + { + StringBuffer buffer = new StringBuffer( "SourceParameters: {" ); + Iterator names = this.getParameterNames(); + String name; + boolean firstName = true; + Iterator values; + String value; + boolean firstValue; + while( names.hasNext() == true ) + { + name = (String)names.next(); + if( firstName == false ) + { + buffer.append( ", " ); + } + else + { + firstName = false; + } + buffer.append( name ).append( " = (" ); + values = this.getParameterValues( name ); + firstValue = true; + while( values.hasNext() == true ) + { + value = (String)values.next(); + if( firstValue == false ) + { + buffer.append( ", " ); + } + else + { + firstValue = false; + } + buffer.append( value ); + } + buffer.append( ')' ); + } + buffer.append( '}' ); + return buffer.toString(); + } + + /** + * Returns a copy of the parameters object. + */ + public Object clone() + { + SourceParameters newObject = new SourceParameters(); + Iterator names = this.getParameterNames(); + Iterator values; + String name, value; + while( names.hasNext() ) + { + name = (String)names.next(); + values = this.getParameterValues( name ); + while( values.hasNext() ) + { + value = (String)values.next(); + newObject.setParameter( name, value ); + } + } + return newObject; + } + + /** + * Test if there are any parameters. + */ + public boolean hasParameters() + { + return ( this.names.size() > 0 ); + } + + /** + * Set the value of this parameter to the given value. + * Remove all other values for this parameter. + */ + public void setSingleParameterValue( String name, String value ) + { + this.removeParameter( name ); + this.setParameter( name, value ); + } + + /** + * Remove all values for this parameter + */ + public void removeParameter( String name ) + { + if( this.names.containsKey( name ) ) + { + this.names.remove( name ); + } + } + + /** + * Returns an immutable java.util.Map containing parameter names as keys and + * parameter values as map values. The keys in the parameter map are of type String. + * The values in the parameter map are of type String array. + */ + public Map getParameterMap() + { + final Map m = new HashMap(this.names); + Iterator entries = m.entrySet().iterator(); + while (entries.hasNext()) + { + Map.Entry entry = (Map.Entry)entries.next(); + ArrayList list = (ArrayList)entry.getValue(); + String[] values = new String[list.size()]; + for(int i=0;iSource + * object, which could then be asked for an InputStream + * etc. + * + * When the Source object is no longer needed + * it must be released using the resolver. This is very similar like + * looking up components from a ComponentLocator. + * In fact a source object can implement most lifecycle interfaces + * like Composable, Initializable, Disposable etc. + * + * @author Avalon Development Team + * @version $Id$ + */ + +public interface SourceResolver +{ + String ROLE = SourceResolver.class.getName(); + + /** With this parameter you can specify the method to use for getting + * the content. It is up to the protocol implementation ({@link + * SourceFactory}) to support this or not + */ + String METHOD = Source.class.getName()+".uri.method"; + + /** With this parameter you can specify additional request parameters which are + * appended to the URI. It is up to the protocol implementation ({@link + * SourceFactory}) to support this or not. + */ + String URI_PARAMETERS = Source.class.getName()+".uri.parameters"; + + /** With this parameter you can specify the encoding to use for encoding + * the additional request parameters the URI. It is up to the protocol + * implementation ({@link SourceFactory}) to support this or not. + */ + String URI_ENCODING = Source.class.getName()+".uri.encoding"; + + /** + * Get a {@link Source} object. This is a shortcut for {@link #resolveURI + * (String, String, Map)}. + * + * @return the resolved source object. + * @throws MalformedURLException if location is malformed. + * @throws IOException if the source couldn't be created for some other reason. + */ + Source resolveURI( String location ) + throws MalformedURLException, IOException; + + /** + * Get a {@link Source} object. + * @param location - the URI to resolve. If this is relative it is either + * resolved relative to the base parameter (if not null) + * or relative to a base setting of the source resolver + * itself. + * @param base - a base URI for resolving relative locations. This + * is optional and can be null. + * @param parameters - Additional parameters for the URI. The parameters + * are specific to the used scheme. + * @return the resolved source object. + * @throws MalformedURLException if location is malformed. + * @throws IOException if the source couldn't be created for some other reason. + */ + Source resolveURI( String location, + String base, + Map parameters ) + throws MalformedURLException, IOException; + + /** + * Releases a resolved resource. + * + * @param source the source to release. + */ + void release( Source source ); +} Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceResolver.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceResolver.java ------------------------------------------------------------------------------ svn:keywords = Id Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceResolver.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceUtil.java URL: http://svn.apache.org/viewvc/cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceUtil.java?rev=639645&view=auto ============================================================================== --- cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceUtil.java (added) +++ cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceUtil.java Fri Mar 21 06:54:32 2008 @@ -0,0 +1,896 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.excalibur.source; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.util.BitSet; +import java.util.Iterator; + +/** + * + * Utility class for source resolving. + * + * @author Avalon Development Team + * @version $Id$ + */ +public final class SourceUtil +{ + private static final char[] alphabet = new char[] + { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0 to 7 + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 8 to 15 + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 16 to 23 + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 24 to 31 + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 32 to 39 + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 40 to 47 + 'w', 'x', 'y', 'z', '0', '1', '2', '3', // 48 to 55 + '4', '5', '6', '7', '8', '9', '+', '/'}; // 56 to 63 + + /** + * Append parameters to the uri + * Each parameter is appended to the uri with "parameter=value", + * the parameters are separated by "&". + */ + public static String appendParameters( String uri, + SourceParameters parameters ) + { + if( parameters != null ) + { + StringBuffer buffer = new StringBuffer( uri ); + Iterator keys = parameters.getParameterNames(); + String current; + char separator = ( uri.indexOf( "?" ) == -1 ? '?' : '&' ); + Iterator values; + + while( keys.hasNext() == true ) + { + current = (String)keys.next(); + values = parameters.getParameterValues( current ); + while( values.hasNext() == true ) + { + buffer.append( separator ) + .append( current ) + .append( '=' ) + .append( SourceUtil.encode( (String)values.next() ) ); + separator = '&'; + } + } + return buffer.toString(); + } + + return uri; + } + + /** + * BASE 64 encoding. + * See also RFC 1421 + */ + public static String encodeBASE64( String s ) + { + return encodeBASE64( s.getBytes() ); + } + + /** + * BASE 64 encoding. + * See also RFC 1421 + */ + public static String encodeBASE64( byte[] octetString ) + { + int bits24; + int bits6; + + char[] out = new char[ ( ( octetString.length - 1 ) / 3 + 1 ) * 4 ]; + + int outIndex = 0; + int i = 0; + + while( ( i + 3 ) <= octetString.length ) + { + // store the octets + bits24 = ( octetString[ i++ ] & 0xFF ) << 16; + bits24 |= ( octetString[ i++ ] & 0xFF ) << 8; + bits24 |= ( octetString[ i++ ] & 0xFF ) << 0; + + bits6 = ( bits24 & 0x00FC0000 ) >> 18; + out[ outIndex++ ] = alphabet[ bits6 ]; + bits6 = ( bits24 & 0x0003F000 ) >> 12; + out[ outIndex++ ] = alphabet[ bits6 ]; + bits6 = ( bits24 & 0x00000FC0 ) >> 6; + out[ outIndex++ ] = alphabet[ bits6 ]; + bits6 = ( bits24 & 0x0000003F ); + out[ outIndex++ ] = alphabet[ bits6 ]; + } + + if( octetString.length - i == 2 ) + { + // store the octets + bits24 = ( octetString[ i ] & 0xFF ) << 16; + bits24 |= ( octetString[ i + 1 ] & 0xFF ) << 8; + + bits6 = ( bits24 & 0x00FC0000 ) >> 18; + out[ outIndex++ ] = alphabet[ bits6 ]; + bits6 = ( bits24 & 0x0003F000 ) >> 12; + out[ outIndex++ ] = alphabet[ bits6 ]; + bits6 = ( bits24 & 0x00000FC0 ) >> 6; + out[ outIndex++ ] = alphabet[ bits6 ]; + + // padding + out[ outIndex++ ] = '='; + } + else if( octetString.length - i == 1 ) + { + // store the octets + bits24 = ( octetString[ i ] & 0xFF ) << 16; + + bits6 = ( bits24 & 0x00FC0000 ) >> 18; + out[ outIndex++ ] = alphabet[ bits6 ]; + bits6 = ( bits24 & 0x0003F000 ) >> 12; + out[ outIndex++ ] = alphabet[ bits6 ]; + + // padding + out[ outIndex++ ] = '='; + out[ outIndex++ ] = '='; + } + + return new String( out ); + } + + /** A BitSet defining the characters which don't need encoding */ + static BitSet charactersDontNeedingEncoding; + static final int characterCaseDiff = ( 'a' - 'A' ); + + /** Initialize the BitSet */ + static + { + charactersDontNeedingEncoding = new BitSet( 256 ); + int i; + for( i = 'a'; i <= 'z'; i++ ) + { + charactersDontNeedingEncoding.set( i ); + } + for( i = 'A'; i <= 'Z'; i++ ) + { + charactersDontNeedingEncoding.set( i ); + } + for( i = '0'; i <= '9'; i++ ) + { + charactersDontNeedingEncoding.set( i ); + } + charactersDontNeedingEncoding.set( '-' ); + charactersDontNeedingEncoding.set( '_' ); + charactersDontNeedingEncoding.set( '.' ); + charactersDontNeedingEncoding.set( '*' ); + charactersDontNeedingEncoding.set( '"' ); + } + + /** + * Translates a string into x-www-form-urlencoded format. + * + * @param s String to be translated. + * @return the translated String. + */ + public static String encode( String s ) + { + final StringBuffer out = new StringBuffer( s.length() ); + final ByteArrayOutputStream buf = new ByteArrayOutputStream( 32 ); + final OutputStreamWriter writer = new OutputStreamWriter( buf ); + for( int i = 0; i < s.length(); i++ ) + { + int c = s.charAt( i ); + if( charactersDontNeedingEncoding.get( c ) ) + { + out.append( (char)c ); + } + else + { + try + { + writer.write( c ); + writer.flush(); + } + catch( IOException e ) + { + buf.reset(); + continue; + } + byte[] ba = buf.toByteArray(); + for( int j = 0; j < ba.length; j++ ) + { + out.append( '%' ); + char ch = Character.forDigit( ( ba[ j ] >> 4 ) & 0xF, 16 ); + // converting to use uppercase letter as part of + // the hex value if ch is a letter. + if( Character.isLetter( ch ) ) + { + ch -= characterCaseDiff; + } + out.append( ch ); + ch = Character.forDigit( ba[ j ] & 0xF, 16 ); + if( Character.isLetter( ch ) ) + { + ch -= characterCaseDiff; + } + out.append( ch ); + } + buf.reset(); + } + } + + return out.toString(); + } + + /** + * Translates a string into x-www-form-urlencoded format + * with specified encoding + * + * @param s String to be translated. + * @param enc The name of a supported charset + * @return the translated String. + * @throws UnsupportedEncodingException + */ + public static String encode( String s, String enc ) throws UnsupportedEncodingException + { + // Why not use the java.net.URLEncoder for this purpose? + final StringBuffer out = new StringBuffer( s.length() ); + final ByteArrayOutputStream buf = new ByteArrayOutputStream( 32 ); + final OutputStreamWriter writer = new OutputStreamWriter( buf, enc ); + for( int i = 0; i < s.length(); i++ ) + { + int c = s.charAt( i ); + if( charactersDontNeedingEncoding.get( c ) ) + { + out.append( (char)c ); + } + else + { + try + { + writer.write( c ); + writer.flush(); + } + catch( IOException e ) + { + buf.reset(); + continue; + } + byte[] ba = buf.toByteArray(); + for( int j = 0; j < ba.length; j++ ) + { + out.append( '%' ); + char ch = Character.forDigit( ( ba[ j ] >> 4 ) & 0xF, 16 ); + // converting to use uppercase letter as part of + // the hex value if ch is a letter. + if( Character.isLetter( ch ) ) + { + ch -= characterCaseDiff; + } + out.append( ch ); + ch = Character.forDigit( ba[ j ] & 0xF, 16 ); + if( Character.isLetter( ch ) ) + { + ch -= characterCaseDiff; + } + out.append( ch ); + } + buf.reset(); + } + } + + return out.toString(); + } + + /** + * Return a File object associated with the Source object. + * + * @return The corresponding File object or null if the + * Source object does not point to a file URI. + */ + public static File getFile( Source source ) + { + final String systemId = source.getURI(); + if( systemId.startsWith( "file:" ) ) + { + return new File( systemId.substring( 5 ) ); + } + return null; + } + + /** + * Move the source to a specified destination. + * + * @param source Source of the source. + * @param destination Destination of the source. + * + * @throws SourceException If an exception occurs during + * the move. + */ + static public void move(Source source, + Source destination) + throws SourceException + { + if (source instanceof MoveableSource + && source.getClass().equals(destination.getClass())) + { + ((MoveableSource)source).moveTo(destination); + } + else if (source instanceof ModifiableSource) + { + copy(source, destination); + ((ModifiableSource) source).delete(); + } + else + { + throw new SourceException("Source '"+source.getURI()+ "' is not writeable"); + } + } + + /** + * Get the position of the scheme-delimiting colon in an absolute URI, as specified + * by RFC 2396, 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. + *

+ * 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 -1 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); + } + + /** + * Copy the source to a specified destination. + * + * @param source Source of the source. + * @param destination Destination of the source. + * + * @throws SourceException If an exception occurs during + * the copy. + */ + static public void copy(Source source, + Source destination) + throws SourceException + { + if (source instanceof MoveableSource + && source.getClass().equals(destination.getClass())) + { + ((MoveableSource) source).copyTo(destination); + } + else + { + if ( !(destination instanceof ModifiableSource)) { + throw new SourceException("Source '"+ + destination.getURI()+ + "' is not writeable"); + } + + IOException firstE = null; + ModifiableSource modDestination = (ModifiableSource)destination; + try + { + InputStream in = source.getInputStream(); + try + { + OutputStream out = modDestination.getOutputStream(); + try + { + try + { + copy(in, out); + } + catch ( IOException e ) + { + // Remebver the original exception in case there are problems closing + // any streams. + firstE = e; + + // If possible, cancel the destination. + if ( modDestination.canCancel( out ) ) + { + modDestination.cancel( out ); + out = null; + } + } + } + finally + { + // out may have already been closed if there was a problem. + if ( out != null ) + { + out.close(); + } + } + } + finally + { + in.close(); + } + } catch (IOException ioe) { + if ( firstE == null ) + { + firstE = ioe; + } + } + + // If there were any problems then wrap the original exception in a SourceException. + if ( firstE != null ) + { + throw new SourceException("Could not copy source '"+ + source.getURI()+"' to '"+ + destination.getURI()+"' :"+ + firstE.getMessage(), firstE); + } + } + } + + /** + * Copy the contents of an InputStream to an OutputStream. + * + * @param in + * @param out + * @throws IOException + */ + static public void copy(InputStream in, OutputStream out) throws IOException + { + byte[] buffer = new byte[8192]; + int length = -1; + + while ((length = in.read(buffer))>-1) { + out.write(buffer, 0, length); + } + in.close(); + out.flush(); + out.close(); + } + + /** + * 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(String, String, boolean, boolean)}. + */ + 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 <segment>/../ 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(String, String, boolean, boolean)}. + */ + 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. + * + *

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): + *

+     * ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
+     *  12            3  4          5       6  7        8 9
+     * 
+ * but without actually using the regular expression. + * + *

The result is returned as a string array, use the constants SCHEME, AUTHORITY, PATH, + * QUERY and FRAGMENT_IDENTIFIER to access the different parts. + * + *

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. + * + *

Interprets %XX (where XX is hexadecimal number) as UTF-8 encoded bytes. + *

The validity of the input path is not checked (i.e. characters that + * were not encoded will not be reported as errors). + *

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(); + } + +} Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceUtil.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceUtil.java ------------------------------------------------------------------------------ svn:keywords = Id Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceUtil.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceValidity.java URL: http://svn.apache.org/viewvc/cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceValidity.java?rev=639645&view=auto ============================================================================== --- cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceValidity.java (added) +++ cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceValidity.java Fri Mar 21 06:54:32 2008 @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.excalibur.source; + +import java.io.Serializable; + +/** + * A SourceValidity object contains all information to check if a Source + * object is still valid. + *

+ * There are two possibilities: + *

    + *
  • The validity object has all information to check by itself if it is valid + * (e.g. given an expires date).
  • + *
  • The validity object possibility needs another (newer) validity object to compare + * against (e.g. to test a last modification date).
  • + *
+ * 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 0 (i.e. "don't know"), then to call {@link #isValid(SourceValidity)}. + *

+ * Remember to call {@link #isValid(SourceValidity)} when {@link #isValid()} returned + * 0 ! + * + * @author Avalon Development Team + * @version $Id$ + */ +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 : + *

    + *
  • -1: invalid. The component isn't valid anymore.
  • + *
  • 0: don't know. This validity should be checked against a new + * validity object using {@link #isValid(SourceValidity)}.
  • + *
  • 1: valid. The component is still valid.
  • + *
+ */ + 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. + *

+ * 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 ); +} Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceValidity.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceValidity.java ------------------------------------------------------------------------------ svn:keywords = Id Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceValidity.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/TraversableSource.java URL: http://svn.apache.org/viewvc/cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/TraversableSource.java?rev=639645&view=auto ============================================================================== --- cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/TraversableSource.java (added) +++ cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/TraversableSource.java Fri Mar 21 06:54:32 2008 @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.excalibur.source; + + +import java.util.Collection; + +/** + * A traversable source is a source that can have children and + * a parent, like a file system. + * + * @author Avalon Development Team + * @version $Id$ + */ +public interface TraversableSource extends Source { + + /** + * Is this source a collection, i.e. it possibly has children ? + * For a filesystem-based implementation, this would typically mean that + * this source represents a directory and not a file. + * + * @return true if the source exists and is traversable. + */ + boolean isCollection(); + + /** + * Get the children of this source if this source is traversable. + *

+ * Note: only those sources actually fetched from the + * collection need to be released using the {@link SourceResolver}. + * + * @see #isCollection() + * @return a collection of {@link Source}s (actually most probably TraversableSources). + * @throws SourceException this source is not traversable, or if some problem occurs. + */ + Collection getChildren() throws SourceException; + + /** + * Get a child of this source, given its name. Note that the returned source + * may not actually physically exist, and that this must be checked using + * {@link Source#exists()}. + * + * @param name the child name. + * @return the child source. + * @throws SourceException if this source is not traversable or if some other + * error occurs. + */ + Source getChild(String name) throws SourceException; + + /** + * Return the name of this source relative to its parent. + * + * @return the name + */ + String getName(); + + /** + * Get the parent of this source as a {@link Source} object. + * + * @return the parent source, or null if this source has no parent. + * @throws SourceException if some problem occurs. + */ + Source getParent() throws SourceException; +} Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/TraversableSource.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/TraversableSource.java ------------------------------------------------------------------------------ svn:keywords = Id Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/TraversableSource.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/URIAbsolutizer.java URL: http://svn.apache.org/viewvc/cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/URIAbsolutizer.java?rev=639645&view=auto ============================================================================== --- cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/URIAbsolutizer.java (added) +++ cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/URIAbsolutizer.java Fri Mar 21 06:54:32 2008 @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.excalibur.source; + +/** + * Implemented by a SourceFactory when it supports applying a relative URI + * to a base URI to form an absolute URI. + * + *

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. + * + * @author Avalon Development Team + * @version $Id$ + */ +public interface URIAbsolutizer +{ + String absolutize(String baseURI, String location); +} Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/URIAbsolutizer.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/URIAbsolutizer.java ------------------------------------------------------------------------------ svn:keywords = Id Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/URIAbsolutizer.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/DynamicURLStreamHandlerFactory.java URL: http://svn.apache.org/viewvc/cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/DynamicURLStreamHandlerFactory.java?rev=639645&view=auto ============================================================================== --- cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/DynamicURLStreamHandlerFactory.java (added) +++ cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/DynamicURLStreamHandlerFactory.java Fri Mar 21 06:54:32 2008 @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.excalibur.sourceresolve.jnet; + +import java.net.URLStreamHandler; +import java.net.URLStreamHandlerFactory; + +public class DynamicURLStreamHandlerFactory extends ParentAwareURLStreamHandlerFactory { + + protected static final ThreadLocal FACTORY = new InheritableThreadLocal(); + + public static void push(URLStreamHandlerFactory factory) { + // no need to synchronize as we use a thread local + if ( !(factory instanceof ParentAwareURLStreamHandlerFactory) ) { + factory = new URLStreamHandlerFactoryWrapper(factory); + } + URLStreamHandlerFactory old = (URLStreamHandlerFactory) FACTORY.get(); + ((ParentAwareURLStreamHandlerFactory)factory).setParentFactory(old); + FACTORY.set(factory); + } + + public static void pop() { + ParentAwareURLStreamHandlerFactory factory = (ParentAwareURLStreamHandlerFactory)FACTORY.get(); + if ( factory != null ) { + FACTORY.set(factory.getParent()); + } + } + + /** + * @see org.apache.excalibur.sourceresolve.jnet.ParentAwareURLStreamHandlerFactory#create(java.lang.String) + */ + protected URLStreamHandler create(String protocol) { + ParentAwareURLStreamHandlerFactory factory = (ParentAwareURLStreamHandlerFactory)FACTORY.get(); + return factory.createURLStreamHandler(protocol); + } +} Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/DynamicURLStreamHandlerFactory.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/DynamicURLStreamHandlerFactory.java ------------------------------------------------------------------------------ svn:keywords = Id Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/DynamicURLStreamHandlerFactory.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/Installer.java URL: http://svn.apache.org/viewvc/cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/Installer.java?rev=639645&view=auto ============================================================================== --- cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/Installer.java (added) +++ cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/Installer.java Fri Mar 21 06:54:32 2008 @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.excalibur.sourceresolve.jnet; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.net.URLStreamHandlerFactory; + +/** + * The installer is a general purpose class to install an own + * {@link URLStreamHandlerFactory} in any environment. + */ +public class Installer { + + public static void setURLStreamHandlerFactory(URLStreamHandlerFactory factory) throws Exception { + try { + // if we can set the factory, its the first! + URL.setURLStreamHandlerFactory(factory); + } catch (Error err) { + Field factoryField = getFactoryField(); + + if (factoryField == null) { + throw new Exception( + "Unable to detect static field in the URL class for the URLStreamHandlerFactory. Please report this error together with your exact environment to the Apache Excalibur project."); + } + try { + URLStreamHandlerFactory oldFactory = (URLStreamHandlerFactory) factoryField.get(null); + if (factory instanceof ParentAwareURLStreamHandlerFactory) { + ((ParentAwareURLStreamHandlerFactory) factory).setParentFactory(oldFactory); + } + factoryField.set(null, factory); + } catch (IllegalArgumentException e) { + throw new Exception("Unable to set url stream handler factory " + factory); + } catch (IllegalAccessException e) { + throw new Exception("Unable to set url stream handler factory " + factory); + } + } + } + + private static Field getFactoryField() { + // let's use reflection to get the field holding the factory + final Field[] fields = URL.class.getDeclaredFields(); + + for (Field current : fields) { + if (Modifier.isStatic(current.getModifiers()) && current.getType().equals(URLStreamHandlerFactory.class)) { + current.setAccessible(true); + return current; + } + } + return null; + + } + +} Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/Installer.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/Installer.java ------------------------------------------------------------------------------ svn:keywords = Id Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/Installer.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/ParentAwareURLStreamHandlerFactory.java URL: http://svn.apache.org/viewvc/cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/ParentAwareURLStreamHandlerFactory.java?rev=639645&view=auto ============================================================================== --- cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/ParentAwareURLStreamHandlerFactory.java (added) +++ cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/ParentAwareURLStreamHandlerFactory.java Fri Mar 21 06:54:32 2008 @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.excalibur.sourceresolve.jnet; + +import java.net.URLStreamHandler; +import java.net.URLStreamHandlerFactory; + +/** + * A parent aware url stream handler factory delegates to a parent + * url stream handler factory, + */ +public abstract class ParentAwareURLStreamHandlerFactory implements URLStreamHandlerFactory { + + protected URLStreamHandlerFactory parentFactory; + + public void setParentFactory(URLStreamHandlerFactory factory) { + this.parentFactory = factory; + } + + public URLStreamHandlerFactory getParent() { + return this.parentFactory; + } + + /** + * @see java.net.URLStreamHandlerFactory#createURLStreamHandler(java.lang.String) + */ + public URLStreamHandler createURLStreamHandler(String protocol) { + URLStreamHandler handler = this.create(protocol); + if ( handler == null && this.parentFactory != null ) { + handler = this.parentFactory.createURLStreamHandler(protocol); + } + return handler; + } + + protected abstract URLStreamHandler create(String protocol); +} Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/ParentAwareURLStreamHandlerFactory.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/ParentAwareURLStreamHandlerFactory.java ------------------------------------------------------------------------------ svn:keywords = Id Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/ParentAwareURLStreamHandlerFactory.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/URLStreamHandlerFactoryWrapper.java URL: http://svn.apache.org/viewvc/cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/URLStreamHandlerFactoryWrapper.java?rev=639645&view=auto ============================================================================== --- cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/URLStreamHandlerFactoryWrapper.java (added) +++ cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/URLStreamHandlerFactoryWrapper.java Fri Mar 21 06:54:32 2008 @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.excalibur.sourceresolve.jnet; + +import java.net.URLStreamHandler; +import java.net.URLStreamHandlerFactory; + +public class URLStreamHandlerFactoryWrapper extends ParentAwareURLStreamHandlerFactory { + + protected final URLStreamHandlerFactory wrapper; + + public URLStreamHandlerFactoryWrapper(URLStreamHandlerFactory f) { + this.wrapper = f; + } + + /** + * @see org.apache.excalibur.sourceresolve.jnet.ParentAwareURLStreamHandlerFactory#create(java.lang.String) + */ + protected URLStreamHandler create(String protocol) { + return this.wrapper.createURLStreamHandler(protocol); + } + + +} Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/URLStreamHandlerFactoryWrapper.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/URLStreamHandlerFactoryWrapper.java ------------------------------------------------------------------------------ svn:keywords = Id Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/URLStreamHandlerFactoryWrapper.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/source/CompositeMap.java URL: http://svn.apache.org/viewvc/cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/source/CompositeMap.java?rev=639645&view=auto ============================================================================== --- cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/source/CompositeMap.java (added) +++ cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/source/CompositeMap.java Fri Mar 21 06:54:32 2008 @@ -0,0 +1,215 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.excalibur.sourceresolve.jnet.source; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * Decorates a map of other maps to provide a single unified view. + *

+ * This map is not modifiable and is not synchronized and is not thread-safe. + */ +public class CompositeMap implements Map { + + /** An empty array. */ + protected static Map[] EMPTY = new Map[0]; + + /** Array of all maps in the composite */ + private Map[] composite = EMPTY; + + /** + * Add an additional Map to the composite. + * + * @param map the Map to be added to the composite + */ + public void pushMap(Map map) { + if ( this.composite.length == 0 ) { + this.composite = new Map[] {map}; + } else { + Map[] temp = new Map[this.composite.length + 1]; + System.arraycopy(this.composite, 0, temp, 0, this.composite.length); + temp[temp.length - 1] = map; + this.composite = temp; + } + } + + /** + * Remove the last map from the composite. + */ + public void popMap() { + if ( this.composite.length == 1 ) { + this.composite = EMPTY; + } else { + Map[] temp = new Map[this.composite.length - 1]; + System.arraycopy(this.composite, 0, temp, 0, this.composite.length - 1); + this.composite = temp; + } + } + + /** + * Return the number of contained maps. + */ + public int getMapCount() { + return this.composite.length; + } + + /** + * @see java.util.Map#clear() + */ + public void clear() { + throw new UnsupportedOperationException("Clear is not supported."); + } + + /** + * @see java.util.Map#containsKey(java.lang.Object) + */ + public boolean containsKey(Object key) { + for (int i = this.composite.length - 1; i >= 0; --i) { + if (this.composite[i].containsKey(key)) { + return true; + } + } + return false; + } + + /** + * @see java.util.Map#containsValue(java.lang.Object) + */ + public boolean containsValue(Object value) { + for (int i = this.composite.length - 1; i >= 0; --i) { + if (this.composite[i].containsValue(value)) { + return true; + } + } + return false; + } + + /** + * @see java.util.Map#entrySet() + */ + public Set entrySet() { + Set entries = new HashSet(); + for (int i = 0; i < this.composite.length; i++) { + entries.addAll(this.composite[i].entrySet()); + } + return entries; + } + + /** + * @see java.util.Map#get(java.lang.Object) + */ + public Object get(Object key) { + for (int i = this.composite.length - 1; i >= 0; --i) { + if (this.composite[i].containsKey(key)) { + return this.composite[i].get(key); + } + } + return null; + } + + /** + * @see java.util.Map#isEmpty() + */ + public boolean isEmpty() { + for (int i = this.composite.length - 1; i >= 0; --i) { + if (!this.composite[i].isEmpty()) { + return false; + } + } + return true; + } + + /** + * @see java.util.Map#keySet() + */ + public Set keySet() { + Set keys = new HashSet(); + for (int i = this.composite.length - 1; i >= 0; --i) { + keys.addAll(this.composite[i].keySet()); + } + return keys; + } + + /** + * @see java.util.Map#put(K, V) + */ + public Object put(Object key, Object value) { + throw new UnsupportedOperationException("Put is not supported"); + } + + /** + * @see java.util.Map#putAll(java.util.Map) + */ + public void putAll(Map map) { + throw new UnsupportedOperationException("Put is not supported"); + } + + /** + * @see java.util.Map#remove(java.lang.Object) + */ + public Object remove(Object key) { + throw new UnsupportedOperationException("Remove is not supported"); + } + + /** + * @see java.util.Map#size() + */ + public int size() { + int size = 0; + for (int i = this.composite.length - 1; i >= 0; --i) { + size += this.composite[i].size(); + } + return size; + } + + /** + * @see java.util.Map#values() + */ + public Collection values() { + Set values = new HashSet(); + for (int i = 0; i < this.composite.length; i++) { + values.addAll(this.composite[i].values()); + } + return values; + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (obj instanceof Map) { + Map map = (Map) obj; + return (this.entrySet().equals(map.entrySet())); + } + return false; + } + + /** + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + int code = 0; + for (Iterator i = this.entrySet().iterator(); i.hasNext();) { + code += i.next().hashCode(); + } + return code; + } +} Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/source/CompositeMap.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/source/CompositeMap.java ------------------------------------------------------------------------------ svn:keywords = Id Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/sourceresolve/jnet/source/CompositeMap.java ------------------------------------------------------------------------------ svn:mime-type = text/plain