You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@struts.apache.org by mr...@apache.org on 2005/11/27 08:10:27 UTC
svn commit: r349187 [5/6] - in /struts/flow/trunk: ./ src/examples/WEB-INF/
src/examples/WEB-INF/guess/ src/examples/WEB-INF/portlet/
src/examples/WEB-INF/remote/ src/examples/remote/ src/java/
src/java/org/apache/struts/flow/ src/java/org/apache/strut...
Added: struts/flow/trunk/src/java/org/apache/struts/flow/core/source/SourceParameters.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/source/SourceParameters.java?rev=349187&view=auto
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/source/SourceParameters.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/source/SourceParameters.java Sat Nov 26 23:10:08 2005
@@ -0,0 +1,458 @@
+/*
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.struts.flow.core.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.struts.flow.core.source.impl.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: SourceParameters.java,v 1.5 2004/03/12 13:02:55 cziegeler Exp $
+ */
+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();
+ }
+
+ /**
+ * 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: struts/flow/trunk/src/java/org/apache/struts/flow/core/source/SourceParameters.java
------------------------------------------------------------------------------
svn:executable = *
Added: struts/flow/trunk/src/java/org/apache/struts/flow/core/source/SourceResolver.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/source/SourceResolver.java?rev=349187&view=auto
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/source/SourceResolver.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/source/SourceResolver.java Sat Nov 26 23:10:08 2005
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.struts.flow.core.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 CVS $Revision: 1.7 $ $Date: 2004/02/28 11:47:26 $
+ */
+
+public interface SourceResolver {
+
+ /** 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";
+
+ void setMethod(String m);
+
+ void setUriParameters(Map params);
+
+ void setUriEncoding(String enc);
+
+
+ /**
+ * 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: struts/flow/trunk/src/java/org/apache/struts/flow/core/source/SourceResolver.java
------------------------------------------------------------------------------
svn:executable = *
Added: struts/flow/trunk/src/java/org/apache/struts/flow/core/source/SourceUtil.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/source/SourceUtil.java?rev=349187&view=auto
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/source/SourceUtil.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/source/SourceUtil.java Sat Nov 26 23:10:08 2005
@@ -0,0 +1,898 @@
+/*
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.struts.flow.core.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 CVS $Revision: 1.5 $ $Date: 2004/02/28 11:47:26 $
+ */
+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 <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.
+ *
+ * <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: struts/flow/trunk/src/java/org/apache/struts/flow/core/source/SourceUtil.java
------------------------------------------------------------------------------
svn:executable = *
Added: struts/flow/trunk/src/java/org/apache/struts/flow/core/source/SourceValidity.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/source/SourceValidity.java?rev=349187&view=auto
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/source/SourceValidity.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/source/SourceValidity.java Sat Nov 26 23:10:08 2005
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.struts.flow.core.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 CVS $Revision: 1.4 $ $Date: 2004/02/28 11:47:26 $
+ */
+public interface SourceValidity
+ extends Serializable
+{
+ final int VALID = +1;
+ final int INVALID = -1;
+ /** @deprecated because it has been misspelled, use UNKNOWN of course */
+ final int UNKNWON = 0;
+ 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: struts/flow/trunk/src/java/org/apache/struts/flow/core/source/SourceValidity.java
------------------------------------------------------------------------------
svn:executable = *
Added: struts/flow/trunk/src/java/org/apache/struts/flow/core/source/TraversableSource.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/source/TraversableSource.java?rev=349187&view=auto
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/source/TraversableSource.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/source/TraversableSource.java Sat Nov 26 23:10:08 2005
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.struts.flow.core.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 CVS $Revision: 1.5 $ $Date: 2004/02/28 11:47:26 $
+ */
+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: struts/flow/trunk/src/java/org/apache/struts/flow/core/source/TraversableSource.java
------------------------------------------------------------------------------
svn:executable = *
Added: struts/flow/trunk/src/java/org/apache/struts/flow/core/source/URIAbsolutizer.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/source/URIAbsolutizer.java?rev=349187&view=auto
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/source/URIAbsolutizer.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/source/URIAbsolutizer.java Sat Nov 26 23:10:08 2005
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.struts.flow.core.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>
+ */
+public interface URIAbsolutizer
+{
+ public String absolutize(String baseURI, String location);
+}
Propchange: struts/flow/trunk/src/java/org/apache/struts/flow/core/source/URIAbsolutizer.java
------------------------------------------------------------------------------
svn:executable = *
Added: struts/flow/trunk/src/java/org/apache/struts/flow/core/source/impl/AbstractSource.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/source/impl/AbstractSource.java?rev=349187&view=auto
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/source/impl/AbstractSource.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/source/impl/AbstractSource.java Sat Nov 26 23:10:08 2005
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.struts.flow.core.source.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.struts.flow.core.source.Source;
+import org.apache.struts.flow.core.source.SourceException;
+import org.apache.struts.flow.core.source.SourceValidity;
+
+/**
+ * Abstract base class for a source implementation.
+ *
+ * @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
+ * @version CVS $Revision: 1.5 $ $Date: 2004/02/28 11:47:24 $
+ */
+
+public abstract class AbstractSource
+ implements Source
+{
+ private boolean m_gotInfos;
+ private long m_lastModificationDate;
+ private long m_contentLength;
+ private String m_systemId;
+
+ private String m_scheme;
+
+ /**
+ * Get the last modification date and content length of the source.
+ * Any exceptions are ignored.
+ * Override this to get the real information
+ */
+ protected void getInfos()
+ {
+ this.m_contentLength = -1;
+ this.m_lastModificationDate = 0;
+ }
+
+ /**
+ * Call {@link #getInfos()} if it hasn't already been called since the last
+ * call to {@link #refresh()}.
+ */
+ protected void checkInfos()
+ {
+ if( !m_gotInfos )
+ {
+ getInfos();
+ m_gotInfos = true;
+ }
+ }
+
+ /**
+ * Return an <code>InputStream</code> object to read from the source.
+ *
+ * The returned stream must be closed by the calling code.
+ *
+ * @throws SourceException if file not found or
+ * HTTP location does not exist.
+ * @throws IOException if I/O error occured.
+ */
+ public InputStream getInputStream()
+ throws IOException, SourceException
+ {
+ return null;
+ }
+
+ /**
+ * Return the unique identifer for this source
+ */
+ public String getURI()
+ {
+ return m_systemId;
+ }
+
+ /**
+ * Return the protocol identifier.
+ */
+ public String getScheme()
+ {
+ return this.m_scheme;
+ }
+
+ /**
+ * Get the Validity object. This can either wrap the last modification
+ * date or the expires information or...
+ * If it is currently not possible to calculate such an information
+ * <code>null</code> is returned.
+ */
+ public SourceValidity getValidity()
+ {
+ return null;
+ }
+
+ /**
+ * Refresh this object and update the last modified date
+ * and content length.
+ */
+ public void refresh()
+ {
+ m_gotInfos = false;
+ }
+
+ /**
+ * The mime-type of the content described by this object.
+ * If the source is not able to determine the mime-type by itself
+ * this can be null.
+ */
+ public String getMimeType()
+ {
+ return null;
+ }
+
+ /**
+ * Return the content length of the content or -1 if the length is
+ * unknown
+ */
+ public long getContentLength()
+ {
+ checkInfos();
+ return this.m_contentLength;
+ }
+
+ /**
+ * Get the last modification date of the source or 0 if it
+ * is not possible to determine the date.
+ */
+ public long getLastModified()
+ {
+ checkInfos();
+ return this.m_lastModificationDate;
+ }
+ /**
+ * Sets the contentLength.
+ * @param contentLength The contentLength to set
+ */
+ protected void setContentLength(long contentLength)
+ {
+ m_contentLength = contentLength;
+ }
+
+ /**
+ * Sets the lastModificationDate.
+ * @param lastModificationDate The lastModificationDate to set
+ */
+ protected void setLastModified(long lastModificationDate)
+ {
+ m_lastModificationDate = lastModificationDate;
+ }
+
+ /**
+ * Sets the scheme.
+ * @param scheme The scheme to set
+ */
+ protected void setScheme(String scheme)
+ {
+ m_scheme = scheme;
+ }
+
+ /**
+ * Sets the systemId.
+ * @param systemId The systemId to set
+ */
+ protected void setSystemId(String systemId)
+ {
+ m_systemId = systemId;
+ }
+
+}
Propchange: struts/flow/trunk/src/java/org/apache/struts/flow/core/source/impl/AbstractSource.java
------------------------------------------------------------------------------
svn:executable = *
Added: struts/flow/trunk/src/java/org/apache/struts/flow/core/source/impl/AbstractSourceResolver.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/source/impl/AbstractSourceResolver.java?rev=349187&view=auto
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/source/impl/AbstractSourceResolver.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/source/impl/AbstractSourceResolver.java Sat Nov 26 23:10:08 2005
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.struts.flow.core.source.impl;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.Map;
+
+import org.apache.struts.flow.core.source.*;
+
+
+/**
+ * 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 CVS $Revision: 1.7 $ $Date: 2004/02/28 11:47:26 $
+ */
+
+public abstract class AbstractSourceResolver implements SourceResolver {
+
+ protected String method;
+ protected Map uriParameters;
+ protected String uriEncoding;
+
+ public void setMethod(String m) {
+ this.method = m;
+ }
+
+ public void setUriParameters(Map params) {
+ this.uriParameters = params;
+ }
+
+ public void setUriEncoding(String enc) {
+ this.uriEncoding = enc;
+ }
+
+
+ /**
+ * 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.
+ */
+ public abstract 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.
+ */
+ public abstract Source resolveURI( String location,
+ String base,
+ Map parameters )
+ throws MalformedURLException, IOException;
+
+ /**
+ * Releases a resolved resource.
+ *
+ * @param source the source to release.
+ */
+ public void release( Source source ) {}
+}
Propchange: struts/flow/trunk/src/java/org/apache/struts/flow/core/source/impl/AbstractSourceResolver.java
------------------------------------------------------------------------------
svn:executable = *
Added: struts/flow/trunk/src/java/org/apache/struts/flow/core/source/impl/ChainSourceResolver.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/source/impl/ChainSourceResolver.java?rev=349187&view=auto
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/source/impl/ChainSourceResolver.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/source/impl/ChainSourceResolver.java Sat Nov 26 23:10:08 2005
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.struts.flow.core.source.impl;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.Map;
+
+import org.apache.commons.chain.web.servlet.ServletWebContext;
+import javax.servlet.ServletContext;
+import org.apache.commons.chain.web.portlet.PortletWebContext;
+import javax.portlet.PortletContext;
+import org.apache.commons.chain.Context;
+
+import java.net.URL;
+
+import org.apache.struts.flow.core.source.*;
+
+
+/**
+ * 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 CVS $Revision: 1.7 $ $Date: 2004/02/28 11:47:26 $
+ */
+
+public class ChainSourceResolver extends AbstractSourceResolver implements SourceResolver {
+
+ private Context ctx;
+
+ public ChainSourceResolver(Context ctx) {
+ this.ctx = ctx;
+ }
+
+ /**
+ * Determines an absolute path to the path name. If the chain context is an
+ * instance of <code>ServletWebContext</code>, the path is first resolved
+ * against the <code>ServletContext</code>. If not, the path is also tested
+ * for being an absolute path already, and if not, tested to see if it is
+ * in the classpath. If either the path is in the <code>ServletContext</code>
+ * or in the classpath, a temp file is created and the path to it is
+ * returned.
+ *
+ *@param ctx The chain context
+ *@param pathname The script path
+ *@return The absolute path to the script file
+ *@exception IOException If anything goes wrong
+ */
+ public Source resolveURI( String location ) throws MalformedURLException, IOException {
+
+ return resolveURI(location, null, null);
+ }
+
+ /*
+ InputStream is = null;
+ File tmpdir = null;
+
+ if (ctx instanceof ServletWebContext) {
+ ServletContext context = ((ServletWebContext) ctx).getContext();
+
+ // Can we access the file in the servlet directory?
+ String path = context.getRealPath(pathname);
+ if (path != null) {
+ File file = new File(path);
+ if (file.exists()) {
+ return (file.toURI());
+ }
+ }
+
+ // Determine the servlet temp directory;
+ tmpdir = (File) context.getAttribute
+ ("javax.servlet.context.tempdir");
+
+ // Try to locate the file in the servlet context
+ is = context.getResourceAsStream(pathname);
+ } else if (ctx instanceof PortletWebContext) {
+ PortletContext context = ((PortletWebContext) ctx).getContext();
+
+ // Can we access the file in the portlet directory?
+ String path = context.getRealPath(pathname);
+ if (path != null) {
+ File file = new File(path);
+ if (file.exists()) {
+ return (file.toURI());
+ }
+ }
+
+ // Try to locate the file in the servlet context
+ is = context.getResourceAsStream(pathname);
+ }
+
+ if (tmpdir == null) {
+ // Set the temp directory
+ tmpdir = new File(System.getProperty("java.io.tmpdir"));
+ }
+
+ // Check to see if the tmp file exists
+ File tmpfile = new File(tmpdir, pathname);
+ if (tmpfile.exists()) {
+ return (tmpfile.toURI());
+ }
+
+ // Check to see if the path name is absolute
+ File file = new File(pathname);
+ if (is != null && file.exists()) {
+ return (file.toURI());
+ }
+
+ // Try to locate the file on the classpath
+ if (is == null) {
+ is = getClass().getResourceAsStream(pathname);
+ }
+
+ // If no source stream is able to be located
+ if (is == null) {
+ throw new IOException("Unable to load resource:" + pathname);
+ }
+
+ // Read from the source stream and create a tmp file
+ BufferedInputStream bis = new BufferedInputStream(is, 1024);
+ FileOutputStream os =
+ new FileOutputStream(tmpfile);
+ BufferedOutputStream bos = new BufferedOutputStream(os, 1024);
+ byte buffer[] = new byte[1024];
+ while (true) {
+ int n = bis.read(buffer);
+ if (n <= 0) {
+ break;
+ }
+ bos.write(buffer, 0, n);
+ }
+ bos.close();
+ bis.close();
+ return (tmpfile.toURI());
+ */
+
+ /**
+ * 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.
+ */
+ public Source resolveURI( String location,
+ String base,
+ Map parameters )
+ throws MalformedURLException, IOException {
+
+ URL url = null;
+ if (ctx instanceof ServletWebContext) {
+ ServletContext context = ((ServletWebContext) ctx).getContext();
+
+ // Can we access the file in the servlet directory?
+ url = context.getResource(location);
+ } else if (ctx instanceof PortletWebContext) {
+ PortletContext context = ((PortletWebContext) ctx).getContext();
+
+ // Can we access the file in the servlet directory?
+ url = context.getResource(location);
+ }
+ if (url != null) {
+ URLSource source = new URLSource();
+ source.init(url, null);
+ return source;
+ } else {
+ return new ResourceSource("resource:/"+location);
+ }
+ }
+
+ /**
+ * Releases a resolved resource.
+ *
+ * @param source the source to release.
+ */
+ public void release( Source source ) {}
+}
Propchange: struts/flow/trunk/src/java/org/apache/struts/flow/core/source/impl/ChainSourceResolver.java
------------------------------------------------------------------------------
svn:executable = *
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org