cocoon-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From reinh...@apache.org
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 GMT
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 <code>Source</code> 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.
+ * <p>
+ * Only particular kinds of <code>Source</code> 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 <code>parameters</code> argument of
+ * {@link SourceResolver#resolveURI(String, String, Map)}.
+ *
+ * @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
+ * @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 <CODE>null</CODE>
+     */
+    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 <CODE>defaultValue</CODE>
+     */
+    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 <CODE>defaultValue</CODE>
+     */
+    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 <CODE>defaultValue</CODE>
+     */
+    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       <CODE>true</CODE> if a value exists, otherwise <CODE>false</CODE>
+     */
+    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;i<values.length;i++)
+            {
+                values[i] = (String)list.get(i);
+            }
+            return values;
+        }
+        return null;
+    }
+
+    /**
+     * Get all parameter names.
+     * @return  Iterator for the (String) parameter names.
+     */
+    public Iterator getParameterNames()
+    {
+        return names.keySet().iterator();
+    }
+
+    /**
+     * Create a map object.
+     * The first value of each parameter is added to the map.
+     * @return A new map - if no parameters are defined this is an
+     *         empty map.
+     */
+    public Map getFirstParametersMap()
+    {
+        final Map result = new HashMap();
+        Iterator iter = this.getParameterNames();
+        String parName;
+        while( iter.hasNext() )
+        {
+            parName = (String)iter.next();
+            result.put( parName, this.getParameter( parName ) );
+        }
+        return result;
+    }
+
+    /**
+     * Build a query string.
+     * 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 <CODE>null</CODE> 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 <CODE>null</CODE> 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;i<values.length;i++)
+            {
+                values[i] = (String)list.get(i);
+            }
+            entry.setValue(values);
+        }
+        return m;
+    }
+}

Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceParameters.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceParameters.java
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceParameters.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceResolver.java
URL: http://svn.apache.org/viewvc/cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceResolver.java?rev=639645&view=auto
==============================================================================
--- cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceResolver.java (added)
+++ cocoon/whiteboard/corona/trunk/corona-servlet/src/main/java/org/apache/excalibur/source/SourceResolver.java Fri Mar 21 06:54:32 2008
@@ -0,0 +1,101 @@
+/*
+ * 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.IOException;
+import java.net.MalformedURLException;
+import java.util.Map;
+
+/**
+ * Base interface for resolving a source by system identifiers.
+ * Instead of using the java.net.URL classes which prevent you
+ * from adding your own custom protocols in a server environment,
+ * you should use this resolver for all URLs.
+ *
+ * The resolver creates for each source a <code>Source</code>
+ * object, which could then be asked for an <code>InputStream</code>
+ * etc.
+ *
+ * When the <code>Source</code> object is no longer needed
+ * it must be released using the resolver. This is very similar like
+ * looking up components from a <code>ComponentLocator</code>.
+ * In fact a source object can implement most lifecycle interfaces
+ * like Composable, Initializable, Disposable etc.
+ *
+ * @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
+ * @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 <code>location</code> 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 <code>null</code>.
+     * @param parameters - Additional parameters for the URI. The parameters
+     *                     are specific to the used scheme.
+     * @return the resolved source object.
+     * @throws MalformedURLException if <code>location</code> 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 <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
+ * @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 <code>x-www-form-urlencoded</code> format.
+     *
+     * @param   s   <code>String</code> to be translated.
+     * @return  the translated <code>String</code>.
+     */
+    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 <code>x-www-form-urlencoded</code> format
+     * with specified encoding
+     *
+     * @param   s   <code>String</code> to be translated.
+     * @param   enc The name of a supported charset
+     * @return  the translated <code>String</code>.
+     * @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 <code>File</code> object associated with the <code>Source</code> object.
+     *
+     * @return The corresponding <code>File</code> object or null if the
+     *         <code>Source</code> 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 <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>, appendix A. This method is
+     * primarily useful for {@link Source} implementors that want to separate
+     * the scheme part from the specific part of an URI.
+     * <p>
+     * Use this method when you need both the scheme and the scheme-specific part of an URI,
+     * as calling successively {@link #getScheme(String)} and {@link #getSpecificPart(String)}
+     * will call this method twice, and as such won't be efficient.
+     *
+     * @param uri the URI
+     * @return int the scheme-delimiting colon, or <code>-1</code> if not found.
+     */
+    public static int indexOfSchemeColon(String uri)
+    {
+        // absoluteURI   = scheme ":" ( hier_part | opaque_part )
+        //
+        // scheme        = alpha *( alpha | digit | "+" | "-" | "." )
+        //
+        // alpha         = lowalpha | upalpha
+        //
+        // lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" |
+        //            "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" |
+        //            "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
+        //
+        // upalpha  = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" |
+        //            "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" |
+        //            "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
+        //
+        // digit    = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
+        //            "8" | "9"
+
+        // Must have at least one character followed by a colon
+        if (uri == null || uri.length() < 2)
+        {
+            return -1;
+        }
+
+        // Check that first character is alpha
+        // (lowercase first since it's the most common case)
+        char ch = uri.charAt(0);
+        if ( (ch < 'a' || ch > 'z') &&
+             (ch < 'A' || ch > 'Z') )
+        {
+            // Invalid first character
+            return -1;
+        }
+
+        int pos = uri.indexOf(':');
+        if (pos != -1)
+        {
+            // Check that every character before the colon is in the allowed range
+            // (the first one was tested above)
+            for (int i = 1; i < pos; i++)
+            {
+                ch = uri.charAt(i);
+                if ( (ch < 'a' || ch > 'z') &&
+                     (ch < 'A' || ch > 'Z') &&
+                     (ch < '0' || ch > '9') &&
+                     ch != '+' && ch != '-' && ch != '.')
+                {
+                    return -1;
+                }
+            }
+        }
+
+        return pos;
+    }
+
+    /**
+     * Get the scheme of an absolute URI.
+     *
+     * @param uri the absolute URI
+     * @return the URI scheme
+     */
+    public static String getScheme(String uri)
+    {
+        int pos = indexOfSchemeColon(uri);
+        return (pos == -1) ? null : uri.substring(0, pos);
+    }
+
+    /**
+     * Get the scheme-specific part of an absolute URI. Note that this includes everything
+     * after the separating colon, including the fragment, if any (RFC 2396 separates it
+     * from the scheme-specific part).
+     *
+     * @param uri the absolute URI
+     * @return the scheme-specific part of the URI
+     */
+    public static String getSpecificPart(String uri)
+    {
+        int pos = indexOfSchemeColon(uri);
+        return (pos == -1) ? null : uri.substring(pos+1);
+    }
+
+    /**
+     * 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 <code>InputStream</code> to an <code>OutputStream</code>.
+     *
+     * @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 &lt;segment&gt;/../ or ./, as described in RFC 2396 in
+     * step 6 of section 5.2.
+     */
+    private static String normalize(String path)
+    {
+        // replace all /./ with /
+        int i = path.indexOf("/./");
+        while (i > -1)
+        {
+            path = path.substring(0, i + 1) + path.substring(i + 3);
+            i = path.indexOf("/./");
+        }
+
+        if (path.endsWith("/."))
+            path = path.substring(0, path.length() - 1);
+
+        int f = path.indexOf("/../");
+        while (f > 0)
+        {
+            int sb = path.lastIndexOf("/", f - 1);
+            if (sb > - 1)
+                path = path.substring(0, sb + 1) + (path.length() >= f + 4 ? path.substring(f + 4) : "");
+            f = path.indexOf("/../");
+        }
+
+        if (path.length() > 3 && path.endsWith("/.."))
+        {
+            int sb = path.lastIndexOf("/", path.length() - 4);
+            String segment = path.substring(sb, path.length() - 3);
+            if (!segment.equals(".."))
+            {
+                path = path.substring(0, sb + 1);
+            }
+        }
+
+        return path;
+    }
+
+    /**
+     * Assembles an URL from the given URL parts, each of these parts can be null.
+     * Used internally by {@link #absolutize(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.
+     *
+     * <p>The parsing is designed to be robust in the sense that it will never fail, even when an invalid
+     * URL is given. The parser will simply look for the most important delimiter characters. Basically
+     * it does the same as what would be achieved using the following regular expression (from RFC 2396):
+     * <pre>
+     * ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
+     *  12            3  4          5       6  7        8 9
+     * </pre>
+     * but without actually using the regular expression.
+     *
+     * <p>The result is returned as a string array, use the constants SCHEME, AUTHORITY, PATH,
+     * QUERY and FRAGMENT_IDENTIFIER to access the different parts.
+     *
+     * <p>If a part is missing, its corresponding entry in the array will be null, except for the
+     * path, which will never be null.
+     */
+    public static String[] parseUrl(String url) {
+        char[] urlchars = url.toCharArray();
+
+        int pos = 0;
+
+        String scheme = null;
+        String authority = null;
+        String path = null;
+        String query = null;
+        String fragid = null;
+
+        //  ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
+
+        // the scheme
+        boolean keepgoing = true;
+        while (keepgoing && pos < urlchars.length)
+        {
+            switch (urlchars[pos])
+            {
+                case ':':
+                    if (pos >= 1)
+                    {
+                        scheme = new String(urlchars, 0, pos);
+                        keepgoing = false;
+                        pos++;
+                        break;
+                    }
+                case '/':
+                case '?':
+                case '#':
+                    keepgoing = false;
+                    break;
+                default:
+                    pos++;
+            }
+        }
+
+        if (scheme == null)
+            pos = 0;
+
+        //  the authority
+        if (pos + 1 < urlchars.length && urlchars[pos] == '/' && urlchars[pos+1] == '/')
+        {
+            pos += 2;
+            int authorityBeginPos = pos;
+            keepgoing = true;
+            while (keepgoing && pos < urlchars.length)
+            {
+                switch (urlchars[pos])
+                {
+                    case '/':
+                    case '?':
+                    case '#':
+                        keepgoing = false;
+                        break;
+                    default:
+                        pos++;
+                }
+            }
+            authority = new String(urlchars, authorityBeginPos, pos - authorityBeginPos);
+        }
+
+        //  the path
+        int pathBeginPos = pos;
+        keepgoing = true;
+        while (keepgoing && pos < urlchars.length)
+        {
+            switch (urlchars[pos])
+            {
+                case '?':
+                case '#':
+                    keepgoing = false;
+                    break;
+                default:
+                    pos++;
+            }
+        }
+        path = new String(urlchars, pathBeginPos, pos - pathBeginPos);
+
+        // the query
+        if (pos < urlchars.length && urlchars[pos] == '?')
+        {
+            pos++;
+            int queryBeginPos = pos;
+            keepgoing = true;
+            while (keepgoing && pos < urlchars.length)
+            {
+                switch (urlchars[pos])
+                {
+                    case '#':
+                        keepgoing = false;
+                        break;
+                    default:
+                        pos++;
+                }
+            }
+            query = new String(urlchars, queryBeginPos, pos - queryBeginPos);
+        }
+
+        // the fragment identifier
+        pos++;
+        if (pos < urlchars.length)
+            fragid = new String(urlchars, pos, urlchars.length - pos);
+
+        return new String[] {scheme, authority, path, query, fragid};
+    }
+
+    /**
+     * Decode a path.
+     *
+     * <p>Interprets %XX (where XX is hexadecimal number) as UTF-8 encoded bytes.
+     * <p>The validity of the input path is not checked (i.e. characters that
+     * were not encoded will not be reported as errors).
+     * <p>This method differs from URLDecoder.decode in that it always uses UTF-8
+     * (while URLDecoder uses the platform default encoding, often ISO-8859-1),
+     * and doesn't translate + characters to spaces.
+     *
+     * @param path the path to decode
+     * @return the decoded path
+     */
+    public static String decodePath(String path) {
+        StringBuffer translatedPath = new StringBuffer(path.length());
+        byte[] encodedchars = new byte[path.length() / 3];
+        int i = 0;
+        int length = path.length();
+        int encodedcharsLength = 0;
+        while (i < length) {
+            if (path.charAt(i) == '%') {
+                // we must process all consecutive %-encoded characters in one go, because they represent
+                // an UTF-8 encoded string, and in UTF-8 one character can be encoded as multiple bytes
+                while (i < length && path.charAt(i) == '%') {
+                    if (i + 2 < length) {
+                        try {
+                            byte x = (byte)Integer.parseInt(path.substring(i + 1, i + 3), 16);
+                            encodedchars[encodedcharsLength] = x;
+                        } catch (NumberFormatException e) {
+                            throw new IllegalArgumentException("Illegal hex characters in pattern %" + path.substring(i + 1, i + 3));
+                        }
+                        encodedcharsLength++;
+                        i += 3;
+                    } else {
+                        throw new IllegalArgumentException("% character should be followed by 2 hexadecimal characters.");
+                    }
+                }
+                try {
+                    String translatedPart = new String(encodedchars, 0, encodedcharsLength, "UTF-8");
+                    translatedPath.append(translatedPart);
+                } catch (UnsupportedEncodingException e) {
+                    // the situation that UTF-8 is not supported is quite theoretical, so throw a runtime exception
+                    throw new RuntimeException("Problem in decodePath: UTF-8 encoding not supported.");
+                }
+                encodedcharsLength = 0;
+            } else {
+                // a normal character
+                translatedPath.append(path.charAt(i));
+                i++;
+            }
+        }
+        return translatedPath.toString();
+    }
+
+}

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 <code>SourceValidity</code> object contains all information to check if a Source
+ * object is still valid.
+ * <p>
+ * There are two possibilities:
+ * <ul>
+ * <li>The validity object has all information to check by itself if it is valid
+ *     (e.g. given an expires date).</li>
+ * <li>The validity object possibility needs another (newer) validity object to compare
+ *     against (e.g. to test a last modification date).</li>
+ * </ul>
+ * To avoid testing what the actual implementation of the validity object supports,
+ * the invocation order is to first call {@link #isValid()} and only if this result
+ * is <code>0</code> (i.e. "don't know"), then to call {@link #isValid(SourceValidity)}.
+ * <p>
+ * Remember to call {@link #isValid(SourceValidity)} when {@link #isValid()} returned
+ * <code>0</code> !
+ *
+ * @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
+ * @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 :
+     * <ul>
+     * <li><code>-1</code>: invalid. The component isn't valid anymore.</li>
+     * <li><code>0</code>: don't know. This validity should be checked against a new
+     *     validity object using {@link #isValid(SourceValidity)}.</li>
+     * <li><code>1</code>: valid. The component is still valid.</li>
+     * </ul>
+     */
+    int isValid();
+
+    /**
+     * Check if the component is still valid. This is only true if the incoming Validity
+     * is of the same type and has the "same" values.
+     * <p>
+     * The invocation order is that the isValid
+     * method of the old Validity object is called with the new one as a
+     * parameter.
+     * @return -1 is returned, if the validity object is not valid anymore
+     *          +1 is returned, if the validity object is still valid
+     *          0  is returned, if the validity check could not be performed.
+     *             In this case, the new validity object is not usable. Examples
+     *             for this are: when the validity objects have different types,
+     *             or when one validity object for any reason is not able to
+     *             get the required information.
+     */
+    int isValid( SourceValidity newValidity );
+}

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 <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
+ * @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.
+     * <p>
+     * <em>Note:</em> 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 <code>TraversableSource</code>s).
+     * @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 <code>null</code> 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.
+ *
+ * <p>If a source factory does not implement this interface, the standard
+ * algorithm (as described in RFC 2396) will be used. This interface only
+ * needs to be implemented for source-types which have a different behaviour.
+ *
+ * @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
+ * @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.
+ * <p>
+ * 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



Mime
View raw message