You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@struts.apache.org by cr...@apache.org on 2005/08/22 04:23:41 UTC

svn commit: r234416 [1/2] - in /struts/sandbox/trunk/tiles/src: java/org/apache/tiles/ java/org/apache/tiles/definition/ java/org/apache/tiles/digester/ java/org/apache/tiles/filter/ java/org/apache/tiles/servlets/ test/org/apache/tiles/ test/org/apach...

Author: craigmcc
Date: Sun Aug 21 19:23:25 2005
New Revision: 234416

URL: http://svn.apache.org/viewcvs?rev=234416&view=rev
Log:
Integrate the "Refactor DefinitionsFactory and Create Reloadable Definitions"
patch.  I had to tweak a few javadoc entries to avoid warnings.

PR:  Bugzilla #36028
Submitted By:  Greg Reddin <gr...@fnf.com>

Added:
    struts/sandbox/trunk/tiles/src/java/org/apache/tiles/ComponentDefinitions.java
    struts/sandbox/trunk/tiles/src/java/org/apache/tiles/DefinitionsReader.java
    struts/sandbox/trunk/tiles/src/java/org/apache/tiles/ReloadableDefinitionsFactory.java
    struts/sandbox/trunk/tiles/src/java/org/apache/tiles/definition/ComponentDefinitionsImpl.java
    struts/sandbox/trunk/tiles/src/java/org/apache/tiles/definition/UrlDefinitionsFactory.java
    struts/sandbox/trunk/tiles/src/java/org/apache/tiles/digester/
    struts/sandbox/trunk/tiles/src/java/org/apache/tiles/digester/DigesterDefinitionsReader.java
    struts/sandbox/trunk/tiles/src/java/org/apache/tiles/filter/
    struts/sandbox/trunk/tiles/src/java/org/apache/tiles/filter/TilesFilter.java
    struts/sandbox/trunk/tiles/src/test/org/apache/tiles/TestComponentDefinitions.java
    struts/sandbox/trunk/tiles/src/test/org/apache/tiles/TestDigesterDefinitionsReader.java
    struts/sandbox/trunk/tiles/src/test/org/apache/tiles/TestReloadableDefinitionsFactory.java
    struts/sandbox/trunk/tiles/src/test/org/apache/tiles/TestUrlDefinitionsFactory.java
    struts/sandbox/trunk/tiles/src/test/org/apache/tiles/config/defs1.xml
    struts/sandbox/trunk/tiles/src/test/org/apache/tiles/config/defs1_FR.xml
    struts/sandbox/trunk/tiles/src/test/org/apache/tiles/config/defs1_en_US.xml
    struts/sandbox/trunk/tiles/src/test/org/apache/tiles/config/defs2.xml
    struts/sandbox/trunk/tiles/src/test/org/apache/tiles/config/defs3.xml
    struts/sandbox/trunk/tiles/src/test/org/apache/tiles/config/invalid-defs.xml
    struts/sandbox/trunk/tiles/src/test/org/apache/tiles/config/malformed-defs.xml
    struts/sandbox/trunk/tiles/src/test/org/apache/tiles/config/temp-defs.xml
    struts/sandbox/trunk/tiles/src/test/org/apache/tiles/mock/
    struts/sandbox/trunk/tiles/src/test/org/apache/tiles/mock/MockComponentDefinitions.java
    struts/sandbox/trunk/tiles/src/test/org/apache/tiles/mock/MockDefinitionsReader.java
Modified:
    struts/sandbox/trunk/tiles/src/java/org/apache/tiles/ComponentDefinition.java
    struts/sandbox/trunk/tiles/src/java/org/apache/tiles/DefinitionsFactory.java
    struts/sandbox/trunk/tiles/src/java/org/apache/tiles/TilesUtil.java
    struts/sandbox/trunk/tiles/src/java/org/apache/tiles/TilesUtilImpl.java
    struts/sandbox/trunk/tiles/src/java/org/apache/tiles/definition/ComponentDefinitionsFactoryWrapper.java
    struts/sandbox/trunk/tiles/src/java/org/apache/tiles/servlets/TilesServlet.java
    struts/sandbox/trunk/tiles/src/test/org/apache/tiles/config/tiles-defs.xml

Modified: struts/sandbox/trunk/tiles/src/java/org/apache/tiles/ComponentDefinition.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/tiles/src/java/org/apache/tiles/ComponentDefinition.java?rev=234416&r1=234415&r2=234416&view=diff
==============================================================================
--- struts/sandbox/trunk/tiles/src/java/org/apache/tiles/ComponentDefinition.java (original)
+++ struts/sandbox/trunk/tiles/src/java/org/apache/tiles/ComponentDefinition.java Sun Aug 21 19:23:25 2005
@@ -20,10 +20,13 @@
 
 import java.io.Serializable;
 import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
 import java.util.Map;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.tiles.xmlDefinition.XmlAttribute;
 import org.apache.tiles.xmlDefinition.XmlDefinition;
 import org.apache.tiles.util.RequestUtils;
 
@@ -35,6 +38,11 @@
 public class ComponentDefinition implements Serializable {
 
     /**
+     * Extends attribute value.
+     */
+    private String inherit;
+    
+    /**
      * Commons Logging instance. 
      */
     protected static Log log = LogFactory.getLog(ComponentDefinition.class);
@@ -68,6 +76,11 @@
      */
     protected String controllerType = null;
 
+    /**
+     * Used for resolving inheritance.
+     */
+    private boolean isVisited=false;
+
     /** 
      * Controller name type. 
      */
@@ -541,4 +554,192 @@
         }
     }
 
+  /**
+   * Add an attribute to this component.
+   *
+   * @param attribute Attribute to add.
+   */
+  public void addAttribute( XmlAttribute attribute)
+    {
+    putAttribute( attribute.getName(), attribute.getValue() );
+    }
+
+  /**
+   * Set extends.
+   *
+   * @param name Name of the extended definition.
+   */
+  public void setExtends(String name)
+    {
+    inherit = name;
+    }
+
+  /**
+   * Get extends.
+   *
+   * @return Name of the extended definition.
+   */
+  public String getExtends()
+    {
+    return inherit;
+    }
+
+  /**
+   * Get extends flag.
+   *
+   */
+  public boolean isExtending( )
+    {
+    return inherit!=null;
+    }
+
+  /**
+   * Set isVisited.
+   *
+   */
+  public void setIsVisited( boolean isVisited )
+    {
+    this.isVisited = isVisited;
+    }
+
+    /**
+     * Resolve inheritance.
+     * First, resolve parent's inheritance, then set path to the parent's path.
+     * Also copy attributes setted in parent, and not set in child
+     * If instance doesn't extend anything, do nothing.
+     * @throws NoSuchDefinitionException If an inheritance can not be solved.
+     */
+  public void resolveInheritance( ComponentDefinitions definitionsSet )
+    throws NoSuchDefinitionException
+    {
+      // Already done, or not needed ?
+    if( isVisited || !isExtending() )
+      return;
+
+    if(log.isDebugEnabled())
+      log.debug( "Resolve definition for child name='" + getName()
+              + "' extends='" + getExtends() + "'.");
+
+      // Set as visited to avoid endless recurisvity.
+    setIsVisited( true );
+
+      // Resolve parent before itself.
+    ComponentDefinition parent = definitionsSet.getDefinition( getExtends() );
+    if( parent == null )
+      { // error
+      String msg = "Error while resolving definition inheritance: child '"
+                           + getName() +    "' can't find its ancestor '"
+                           + getExtends() + "'. Please check your description file.";
+      log.error( msg );
+        // to do : find better exception
+      throw new NoSuchDefinitionException( msg );
+      }
+
+    parent.resolveInheritance( definitionsSet );
+
+      // Iterate on each parent's attribute and add it if not defined in child.
+    Iterator parentAttributes = parent.getAttributes().keySet().iterator();
+    while( parentAttributes.hasNext() )
+      {
+      String name = (String)parentAttributes.next();
+      if( !getAttributes().containsKey(name) )
+        putAttribute( name, parent.getAttribute(name) );
+      }
+      // Set path and role if not setted
+    if( path == null )
+      setPath( parent.getPath() );
+    if( role == null )
+      setRole( parent.getRole() );
+    if( controller==null )
+      {
+      setController( parent.getController());
+      setControllerType( parent.getControllerType());
+      }
+    }
+
+    /**
+     * Resolve locale-specific inheritance.
+     * First, resolve parent's inheritance, then set path to the parent's path.
+     * Also copy attributes setted in parent, and not set in child
+     * If instance doesn't extend anything, do nothing.
+     * @throws NoSuchDefinitionException If an inheritance can not be solved.
+     */
+  public void resolveInheritance( ComponentDefinitions definitionsSet, Locale locale)
+    throws NoSuchDefinitionException
+    {
+      // Already done, or not needed ?
+    if( isVisited || !isExtending() )
+      return;
+
+    if(log.isDebugEnabled())
+      log.debug( "Resolve definition for child name='" + getName()
+              + "' extends='" + getExtends() + "'.");
+
+      // Set as visited to avoid endless recurisvity.
+    setIsVisited( true );
+
+      // Resolve parent before itself.
+    ComponentDefinition parent = definitionsSet.getDefinition( getExtends(), 
+            locale );
+    if( parent == null )
+      { // error
+      String msg = "Error while resolving definition inheritance: child '"
+                           + getName() +    "' can't find its ancestor '"
+                           + getExtends() + "'. Please check your description file.";
+      log.error( msg );
+        // to do : find better exception
+      throw new NoSuchDefinitionException( msg );
+      }
+
+    parent.resolveInheritance( definitionsSet, locale );
+
+      // Iterate on each parent's attribute and add it if not defined in child.
+    Iterator parentAttributes = parent.getAttributes().keySet().iterator();
+    while( parentAttributes.hasNext() )
+      {
+      String name = (String)parentAttributes.next();
+      if( !getAttributes().containsKey(name) )
+        putAttribute( name, parent.getAttribute(name) );
+      }
+      // Set path and role if not setted
+    if( path == null )
+      setPath( parent.getPath() );
+    if( role == null )
+      setRole( parent.getRole() );
+    if( controller==null )
+      {
+      setController( parent.getController());
+      setControllerType( parent.getControllerType());
+      }
+    }
+
+  /**
+   * Overload this definition with passed child.
+   * All attributes from child are copied to this definition. Previous attributes with
+   * same name are disguarded.
+   * Special attribute 'path','role' and 'extends' are overloaded if defined in child.
+   * @param child Child used to overload this definition.
+   */
+  public void overload( ComponentDefinition child )
+    {
+    if( child.getPath() != null )
+      {
+      path = child.getPath();
+      }
+    if( child.getExtends() != null )
+      {
+      inherit = child.getExtends();
+      }
+    if( child.getRole() != null )
+      {
+      role = child.getRole();
+      }
+    if( child.getController()!=null )
+      {
+      controller = child.getController();
+      controllerType =  child.getControllerType();
+      }
+      // put all child attributes in parent.
+    attributes.putAll( child.getAttributes());
+    }
 }

Added: struts/sandbox/trunk/tiles/src/java/org/apache/tiles/ComponentDefinitions.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/tiles/src/java/org/apache/tiles/ComponentDefinitions.java?rev=234416&view=auto
==============================================================================
--- struts/sandbox/trunk/tiles/src/java/org/apache/tiles/ComponentDefinitions.java (added)
+++ struts/sandbox/trunk/tiles/src/java/org/apache/tiles/ComponentDefinitions.java Sun Aug 21 19:23:25 2005
@@ -0,0 +1,95 @@
+/*
+ * $Id$
+ *
+ * Copyright 1999-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.tiles;
+
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Interface for managing collections of {@link ComponentDefinition} objects.
+ *
+ * <p>The ComponentDefinitions interface provides a pattern for managing 
+ * ComponentDefinition objects.  Implementations will provide a means to append
+ * new ComponentDefinitions to the collection, add and retrieve lcale-specific
+ * ComponentDefinitions objects, and reset the collections.</p>
+ * 
+ * @version $Rev$ $Date$ 
+ */
+public interface ComponentDefinitions {
+    
+    /**
+     * Returns a ComponentDefinition object that matches the given name.
+     *
+     * @param name The name of the ComponentDefinition to return.
+     * @return the ComponentDefinition matching the given name or null if none
+     *  is found.
+     */
+    public ComponentDefinition getDefinition(String name);
+    
+    /**
+     * Adds new ComponentDefinition objects to the internal collection and 
+     * resolves inheritance attraibutes.
+     *
+     * @param defsMap The new definitions to add.
+     * @throws NoSuchDefinitionException if a ComponentDefinition extends from
+     * one that doesn't exist.
+     */
+    public void addDefinitions(Map defsMap) throws NoSuchDefinitionException ;
+
+    /**
+     * Adds new locale-specific ComponentDefinition objects to the internal 
+     * collection and resolves inheritance attraibutes.
+     *
+     * @param defsMap The new definitions to add.
+     * @param locale The locale to add the definitions to.
+     * @throws NoSuchDefinitionException if a ComponentDefinition extends from
+     * one that doesn't exist.
+     */
+    public void addDefinitions(Map defsMap, Locale locale) throws NoSuchDefinitionException ;
+
+    /**
+     * Returns a ComponentDefinition object that matches the given name and locale.
+     *
+     * @param name The name of the ComponentDefinition to return.
+     * @param locale The locale to use to resolve the definition.
+     * @return the ComponentDefinition matching the given name or null if none
+     *  is found.
+     */
+    public ComponentDefinition getDefinition(String name, Locale locale);
+    
+    /**
+     * Resolves configuration inheritance properties.
+     */
+    public void resolveInheritances() throws NoSuchDefinitionException;
+    
+    /**
+     * Resolves locale-specific configuration inheritance properties.
+     */
+    public void resolveInheritances(Locale locale) throws NoSuchDefinitionException;
+    
+    /**
+     * Clears definitions.
+     */
+    public void reset();
+
+    /**
+     * Returns base definitions collection;
+     */
+    public Map getBaseDefinitions();
+}

Modified: struts/sandbox/trunk/tiles/src/java/org/apache/tiles/DefinitionsFactory.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/tiles/src/java/org/apache/tiles/DefinitionsFactory.java?rev=234416&r1=234415&r2=234416&view=diff
==============================================================================
--- struts/sandbox/trunk/tiles/src/java/org/apache/tiles/DefinitionsFactory.java (original)
+++ struts/sandbox/trunk/tiles/src/java/org/apache/tiles/DefinitionsFactory.java Sun Aug 21 19:23:25 2005
@@ -16,81 +16,95 @@
  * limitations under the License.
  */
 
-
 package org.apache.tiles;
 
-import java.io.Serializable;
-
-import javax.servlet.ServletContext;
-import javax.servlet.ServletRequest;
+import java.util.Locale;
+import java.util.Map;
 
 /**
- * Tiles Definition factory.
- * This interface replace old ComponentDefinitionsFactory.
- * Main method getDefinition() is exactly the same. Initialization method change.
- * This interface allows to retrieve a definition by its name, independently of
- * the factory implementation.
- * Object life cycle is as follow:
- * <ul>
- * <li>Constructor: create object</li>
- * <li>setConfig: set config and initialize factory. After first call to this
- * method, factory is operational.</li>
- * <li>destroy: factory is being shutdown.</li>
- * </ul>
- * Implementation must be Serializable, in order to be compliant with web Container
- * having this constraint (Weblogic 6.x).
+ * Interface for creating a {@link ComponentDefinitions} object and managing
+ * its contents.
+ *
+ * <p>DefinitionsFactory implementations are responsible for maintaining the data
+ * sources of Tiles configuration data and using the data to create 
+ * ComponentDefinitions sets.  Implementations also know how to append
+ * locale-specific configuration data to an existing ComponentDefinitions set.</p>
+ *
+ *
+ * @version $Rev$ $Date$ 
  */
-public interface DefinitionsFactory extends Serializable
-{
-
-   /**
-     * Get a definition by its name.
-     * @param name Name of requested definition.
-     * @param request Current servelet request
-     * @param servletContext current servlet context
-     * @throws DefinitionsFactoryException An error occur while getting definition.
-     * @throws NoSuchDefinitionException No definition found for specified name
-     * Implementation can throw more accurate exception as a subclass of this exception
-   */
-   public ComponentDefinition getDefinition(String name, ServletRequest request, ServletContext servletContext)
-     throws NoSuchDefinitionException,DefinitionsFactoryException;
-
-   /**
-    * Init definition factory.
-    * This method is called immediately after factory creation, and prior any call
-    * to setConfig().
-    *
-    * @param config Configuration object used to set factory configuration.
-    * @param servletContext Servlet Context passed to factory.
-    * @throws DefinitionsFactoryException An error occur during initialization.
-    */
-   public void init(DefinitionsFactoryConfig config, ServletContext servletContext)
-     throws DefinitionsFactoryException;
-
+public interface DefinitionsFactory {
+    
     /**
-     * <p>Receive notification that the factory is being
-     * shut down.</p>
+     * Property name that specifies the implementation of the DefinitionsReader.
      */
-    public void destroy();
-
-   /**
-    * Set factory configuration.
-    * This method is used to change factory configuration.
-    * This method is optional, and can send an exception if implementation
-    * doesn't allow change in configuration.
-    *
-    * @param config Configuration object used to set factory configuration.
-    * @param servletContext Servlet Context passed to factory.
-    * @throws DefinitionsFactoryException An error occur during initialization.
-    */
-   public void setConfig(DefinitionsFactoryConfig config, ServletContext servletContext)
-     throws DefinitionsFactoryException;
-
-   /**
-    * Get factory configuration.
-    * @return TilesConfig
-    */
-   public DefinitionsFactoryConfig getConfig();
-
+    public static final String READER_IMPL_PROPERTY =
+            "org.apache.tiles.DefinitionsReader";
+    /**
+     * Property name that specifies the implementation of ComponentDefinitions.
+     */
+    public static final String DEFINITIONS_IMPL_PROPERTY =
+            "org.apache.tiles.ComponentDefinitions";
+    
+    /**
+     * Initializes the DefinitionsFactory and its subcomponents.
+     *
+     * Implementations may support configuration properties to be passed in via
+     * the params Map.
+     *
+     * @param params The Map of configuration properties.
+     * @throws DefinitionsFactoryException if an initialization error occurs.
+     */
+    public void init(Map params) throws DefinitionsFactoryException;
+    
+    /**
+     * Adds a source where ComponentDefinition objects are stored.
+     * 
+     * Implementations should publish what type of source object they expect.
+     * The source should contain enough information to resolve a configuration
+     * source containing definitions.  The source should be a "base" source for
+     * configurations.  Internationalization and Localization properties will be
+     * applied by implementations to discriminate the correct data sources based
+     * on locale.
+     * 
+     * @param source The configuration source for definitions.
+     * @throws DefinitionsFactoryException if an invalid source is passed in or
+     *      an error occurs resolving the source to an actual data store.
+     */
+    public void addSource(Object source) throws DefinitionsFactoryException;
+    
+    /**
+     * Creates and returns a {@link ComponentDefinitions} set by reading 
+     * configuration data from the applied sources.
+     *
+     * @throws DefinitionsFactoryException if an error occurs reading the 
+     *      sources.
+     */
+    public ComponentDefinitions readDefinitions() 
+            throws DefinitionsFactoryException;
 
+    /**
+     * Appends locale-specific {@link ComponentDefinition} objects to an existing
+     * {@link ComponentDefinitions} set by reading locale-specific versions of
+     * the applied sources.
+     *
+     * @param definitions The ComponentDefinitions object to append to.
+     * @param locale The requested locale.
+     * @throws DefinitionsFactoryException if an error occurs reading definitions.
+     */
+    public void addDefinitions(ComponentDefinitions definitions, Locale locale) 
+            throws DefinitionsFactoryException;
+    
+    /**
+     * Indicates whether a given locale has been processed or not.
+     * 
+     * This method can be used to avoid unnecessary synchronization of the
+     * DefinitionsFactory in multi-threaded situations.  Check the return of
+     * isLoacaleProcessed before synchronizing the object and reading 
+     * locale-specific definitions.
+     *
+     * @param locale The locale to check.
+     * @return true if the given lcoale has been processed and false otherwise.
+     */
+    public boolean isLocaleProcessed(Locale locale);
 }

Added: struts/sandbox/trunk/tiles/src/java/org/apache/tiles/DefinitionsReader.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/tiles/src/java/org/apache/tiles/DefinitionsReader.java?rev=234416&view=auto
==============================================================================
--- struts/sandbox/trunk/tiles/src/java/org/apache/tiles/DefinitionsReader.java (added)
+++ struts/sandbox/trunk/tiles/src/java/org/apache/tiles/DefinitionsReader.java Sun Aug 21 19:23:25 2005
@@ -0,0 +1,63 @@
+/*
+ * $Id$
+ *
+ * Copyright 1999-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.tiles;
+
+import java.util.Map;
+
+/**
+ * Interface for reading <code>{@link ComponentDefinition}</code> from a source.
+ *
+ * <p>This interface provides a standard way to read 
+ * <code>{@link ComponentDefinition}</code> objects from a source.  Implementations
+ * should define what the source is, whether it be a persistent store such as a
+ * configuration file or database, or something like a web service.  The 
+ * DefinitionsReader is responsible for reading from a single location.  It does
+ * not perform any internationalization duties or inheritance of ComponentDefinitions.
+ * It only reads from the source and returns a Map of objects read.</p>
+ *
+ * @version $Rev$ $Date$ 
+ */
+public interface DefinitionsReader {
+
+    /**
+     * Initializes the <code>DefinitionsReader</code> object.
+     *
+     * This method must be called before the {@link #read(java.lang.Object)} method is called.
+     *
+     * @param params A map of properties used to set up the reader.
+     * @throws DefinitionsFactoryException if required properties are not
+     *  passed in or the initialization fails.
+     *
+     */
+    public void init(Map params) throws DefinitionsFactoryException;
+    
+    /**
+     * Reads <code>{@link ComponentDefinition}</code> objects from a source.
+     *
+     * Implementations should publish what type of source object is expected.
+     *
+     * @param source The source from which definitions will be read.
+     * @return a Map of <code>ComponentDefinition</code> objects read from
+     *  the source.
+     * @throws DefinitionsFactoryException if the source is invalid or
+     *  an error occurs when reading definitions.
+     */
+    public Map read(Object source) throws DefinitionsFactoryException;
+    
+}

Added: struts/sandbox/trunk/tiles/src/java/org/apache/tiles/ReloadableDefinitionsFactory.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/tiles/src/java/org/apache/tiles/ReloadableDefinitionsFactory.java?rev=234416&view=auto
==============================================================================
--- struts/sandbox/trunk/tiles/src/java/org/apache/tiles/ReloadableDefinitionsFactory.java (added)
+++ struts/sandbox/trunk/tiles/src/java/org/apache/tiles/ReloadableDefinitionsFactory.java Sun Aug 21 19:23:25 2005
@@ -0,0 +1,34 @@
+/*
+ * $Id$
+ *
+ * Copyright 1999-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.tiles;
+
+/**
+ * Indicates support for reloading Tiles configuration when it changes.
+ *
+ * @version $Rev$ $Date$ 
+ */
+public interface ReloadableDefinitionsFactory {
+    
+    /**
+     * Indicates whether the DefinitionsFactory is out of date and needs to be
+     * reloaded.
+     */
+    public boolean refreshRequired();
+}

Modified: struts/sandbox/trunk/tiles/src/java/org/apache/tiles/TilesUtil.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/tiles/src/java/org/apache/tiles/TilesUtil.java?rev=234416&r1=234415&r2=234416&view=diff
==============================================================================
--- struts/sandbox/trunk/tiles/src/java/org/apache/tiles/TilesUtil.java (original)
+++ struts/sandbox/trunk/tiles/src/java/org/apache/tiles/TilesUtil.java Sun Aug 21 19:23:25 2005
@@ -196,15 +196,7 @@
         ServletContext servletContext)
         throws FactoryNotFoundException, DefinitionsFactoryException {
             
-        try {
-            return getDefinitionsFactory(request, servletContext).getDefinition(
-                definitionName,
-                (HttpServletRequest) request,
-                servletContext);
-                
-        } catch (NullPointerException ex) { // Factory not found in context
-            throw new FactoryNotFoundException("Can't get definitions factory from context.");
-        }
+        return tilesUtilImpl.getDefinition(definitionName, request, servletContext);
     }
 
     /**

Modified: struts/sandbox/trunk/tiles/src/java/org/apache/tiles/TilesUtilImpl.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/tiles/src/java/org/apache/tiles/TilesUtilImpl.java?rev=234416&r1=234415&r2=234416&view=diff
==============================================================================
--- struts/sandbox/trunk/tiles/src/java/org/apache/tiles/TilesUtilImpl.java (original)
+++ struts/sandbox/trunk/tiles/src/java/org/apache/tiles/TilesUtilImpl.java Sun Aug 21 19:23:25 2005
@@ -23,6 +23,11 @@
 
 import java.lang.reflect.Method;
 import java.lang.reflect.InvocationTargetException;
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
 
 import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
@@ -49,6 +54,10 @@
     /** Constant name used to store factory in servlet context */
     public static final String DEFINITIONS_FACTORY =
         "org.apache.tiles.DEFINITIONS_FACTORY";
+    
+    /** Constant used to store ComponentDefinitions graph. */
+    public static final String DEFINITIONS_OBJECT = 
+            "org.apache.tiles.ComponentDefinitions";
         
     /**
      * JSP 2.0 include method to use which supports configurable flushing.
@@ -165,17 +174,71 @@
         DefinitionsFactoryConfig factoryConfig)
         throws DefinitionsFactoryException {
             
+        // FIXME:  Pull from servlet context.
+        String factoryClassName = "org.apache.tiles.definition.UrlDefinitionsFactory";
+        
         // Create configurable factory
         DefinitionsFactory factory =
-            createDefinitionFactoryInstance(factoryConfig.getFactoryClassname());
-            
-        factory.init(factoryConfig, servletContext);
+            createDefinitionFactoryInstance(factoryClassName);
+
+        Map params = factoryConfig.getAttributes();
+        
+        factory.init(params);
+
+        String configFiles = factoryConfig.getDefinitionConfigFiles();
+        List filenames = getFilenames(configFiles);
+        
+        try {
+            for (int i = 0; i < filenames.size(); i++) {
+                String filename = (String) filenames.get(i);
+                factory.addSource(servletContext.getResource(filename));
+            }
+        } catch (MalformedURLException e) {
+            throw new DefinitionsFactoryException("Problem with filename URL: ", e);
+        }
+        
+        ComponentDefinitions definitions = factory.readDefinitions();
         
         // Make factory accessible from jsp tags (push it in appropriate context)
         makeDefinitionsFactoryAccessible(factory, servletContext);
+        makeDefinitionsAccessible(definitions, servletContext);
+        
         return factory;
     }
 
+    public ComponentDefinition getDefinition(String definitionName,
+            ServletRequest request,
+            ServletContext servletContext) 
+            throws FactoryNotFoundException, DefinitionsFactoryException {
+        
+        try {
+            DefinitionsFactory factory = getDefinitionsFactory(request, servletContext);
+            ComponentDefinitions definitions = (ComponentDefinitions) 
+                servletContext.getAttribute(TilesUtilImpl.DEFINITIONS_OBJECT);
+            ComponentDefinition definition = definitions.getDefinition(
+                    definitionName, request.getLocale());
+            
+            if (definition == null) {
+                if (!factory.isLocaleProcessed(request.getLocale())) {
+                    // FIXME This will modify the factory as well as the definitions
+                    // but we are only locking the definitions.
+                    // 
+                    // We'll have to refactor again to remove this issue.
+                    synchronized (definitions) {
+                        factory.addDefinitions(definitions, request.getLocale());
+                    }
+                }
+                
+                definition = definitions.getDefinition(
+                    definitionName, request.getLocale());
+            }
+            
+            return definition;
+        } catch (NullPointerException ex) { // Factory not found in context
+            throw new FactoryNotFoundException("Can't get definitions factory from context.");
+        }
+    }
+    
     /**
      * Create Definition factory of specified classname.
      * Factory class must extend the {@link DefinitionsFactory} class.
@@ -205,7 +268,7 @@
             throw new DefinitionsFactoryException(
                 "Error - createDefinitionsFactory : Factory class '"
                     + classname
-                    + " must implement 'TilesDefinitionsFactory'.",
+                    + " must implement 'DefinitionsFactory'.",
                 ex);
                 
         } catch (ClassNotFoundException ex) { // Bad classname
@@ -236,4 +299,30 @@
         servletContext.setAttribute(DEFINITIONS_FACTORY, factory);
     }
 
-}
\ No newline at end of file
+    /**
+     * Make definition factory accessible to Tags.
+     * Factory is stored in servlet context.
+     * @param definitions Definition factory to be made accessible
+     * @param servletContext Current servlet context.
+     */
+    protected void makeDefinitionsAccessible(
+        ComponentDefinitions definitions,
+        ServletContext servletContext) {
+            
+        servletContext.setAttribute(DEFINITIONS_OBJECT, definitions);
+    }
+
+    /**
+     * Parses a comma-delimited string for a list of config filenames.
+     */
+    protected List getFilenames(String filenameString) {
+        // Init list of filenames
+        StringTokenizer tokenizer = new StringTokenizer(filenameString, ",");
+        List filenames = new ArrayList(tokenizer.countTokens());
+        while (tokenizer.hasMoreTokens()) {
+            filenames.add(tokenizer.nextToken().trim());
+        }
+        
+        return filenames;
+    }
+}

Modified: struts/sandbox/trunk/tiles/src/java/org/apache/tiles/definition/ComponentDefinitionsFactoryWrapper.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/tiles/src/java/org/apache/tiles/definition/ComponentDefinitionsFactoryWrapper.java?rev=234416&r1=234415&r2=234416&view=diff
==============================================================================
--- struts/sandbox/trunk/tiles/src/java/org/apache/tiles/definition/ComponentDefinitionsFactoryWrapper.java (original)
+++ struts/sandbox/trunk/tiles/src/java/org/apache/tiles/definition/ComponentDefinitionsFactoryWrapper.java Sun Aug 21 19:23:25 2005
@@ -37,7 +37,7 @@
  * This class provides mapping from the old interface's life cycle to the new life cycle.
  * @since 20020708
  */
-public class ComponentDefinitionsFactoryWrapper implements DefinitionsFactory {
+public class ComponentDefinitionsFactoryWrapper /*FIXME: implements DefinitionsFactory*/ {
 
     /** 
      * The underlying factory. 

Added: struts/sandbox/trunk/tiles/src/java/org/apache/tiles/definition/ComponentDefinitionsImpl.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/tiles/src/java/org/apache/tiles/definition/ComponentDefinitionsImpl.java?rev=234416&view=auto
==============================================================================
--- struts/sandbox/trunk/tiles/src/java/org/apache/tiles/definition/ComponentDefinitionsImpl.java (added)
+++ struts/sandbox/trunk/tiles/src/java/org/apache/tiles/definition/ComponentDefinitionsImpl.java Sun Aug 21 19:23:25 2005
@@ -0,0 +1,146 @@
+/*
+ * $Id$
+ *
+ * Copyright 1999-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.tiles.definition;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import org.apache.tiles.ComponentDefinition;
+import org.apache.tiles.ComponentDefinitions;
+import org.apache.tiles.NoSuchDefinitionException;
+
+/**
+ *
+ * @version $Rev$ $Date$ 
+ */
+public class ComponentDefinitionsImpl implements ComponentDefinitions {
+    /**
+     * The base set of ComponentDefinition objects not discriminated by locale.
+     */
+    private Map baseDefinitions;
+    /**
+     * The locale-specific set of definitions objects.
+     */
+    private Map localeSpecificDefinitions;
+    
+    /** Creates a new instance of ComponentDefinitionsImpl */
+    public ComponentDefinitionsImpl() {
+        baseDefinitions = new HashMap();
+        localeSpecificDefinitions = new HashMap();
+    }
+
+    /**
+     * Returns a ComponentDefinition object that matches the given name.
+     * 
+     * @param name The name of the ComponentDefinition to return.
+     * @return the ComponentDefinition matching the given name or null if none
+     *  is found.
+     */
+    public ComponentDefinition getDefinition(String name) {
+        return (ComponentDefinition) baseDefinitions.get(name);
+    }
+
+    /**
+     * Adds new ComponentDefinition objects to the internal collection and 
+     * resolves inheritance attraibutes.
+     * 
+     * @param defsMap The new definitions to add.
+     */
+    public void addDefinitions(Map defsMap) throws NoSuchDefinitionException {
+        this.baseDefinitions.putAll(defsMap);
+        resolveInheritances();
+    }
+
+    /**
+     * Adds new locale-specific ComponentDefinition objects to the internal 
+     * collection and resolves inheritance attraibutes.
+     *
+     * @param defsMap The new definitions to add.
+     * @param locale The locale to add the definitions to.
+     */
+    public void addDefinitions(Map defsMap, Locale locale) throws NoSuchDefinitionException {
+        localeSpecificDefinitions.put(locale, defsMap);
+        resolveInheritances(locale);
+    }
+    
+    /**
+     * Returns a ComponentDefinition object that matches the given name and locale.
+     *
+     * @param name The name of the ComponentDefinition to return.
+     * @param locale The locale to use to resolve the definition.
+     * @return the ComponentDefinition matching the given name or null if none
+     *  is found.
+     */
+    public ComponentDefinition getDefinition(String name, Locale locale) {
+        ComponentDefinition definition = null;
+        Map localeSpecificMap = (Map) localeSpecificDefinitions.get(locale);
+        if (localeSpecificMap != null) {
+            definition = (ComponentDefinition) localeSpecificMap.get(name);
+        }
+        
+        if (definition == null) {
+            definition = getDefinition(name);
+        }
+        
+        return definition;
+    }
+    
+    /**
+     * Resolve extended instances.
+     */
+    public void resolveInheritances() throws NoSuchDefinitionException {
+        Iterator i = baseDefinitions.values().iterator();
+        while( i.hasNext() ) {
+            ComponentDefinition definition = (ComponentDefinition)i.next();
+            definition.resolveInheritance( this );
+        }  // end loop
+    }
+    
+    /**
+     * Resolve locale-specific extended instances.
+     */
+    public void resolveInheritances(Locale locale) throws NoSuchDefinitionException {
+        resolveInheritances();
+        
+        Map map = (Map) localeSpecificDefinitions.get(locale);
+        if (map != null) {
+            Iterator i = map.values().iterator();
+            while( i.hasNext() ) {
+                ComponentDefinition definition = (ComponentDefinition)i.next();
+                definition.resolveInheritance( this, locale );
+            }  // end loop
+        }
+    }
+    
+    /**
+     * Clears definitions.
+     */
+    public void reset() {
+        this.baseDefinitions = new HashMap();
+        this.localeSpecificDefinitions = new HashMap();
+    }
+
+    /**
+     * Returns base definitions collection;
+     */
+    public Map getBaseDefinitions() {
+        return this.baseDefinitions;
+    }
+}

Added: struts/sandbox/trunk/tiles/src/java/org/apache/tiles/definition/UrlDefinitionsFactory.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/tiles/src/java/org/apache/tiles/definition/UrlDefinitionsFactory.java?rev=234416&view=auto
==============================================================================
--- struts/sandbox/trunk/tiles/src/java/org/apache/tiles/definition/UrlDefinitionsFactory.java (added)
+++ struts/sandbox/trunk/tiles/src/java/org/apache/tiles/definition/UrlDefinitionsFactory.java Sun Aug 21 19:23:25 2005
@@ -0,0 +1,386 @@
+/*
+ * $Id$
+ *
+ * Copyright 1999-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.tiles.definition;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import org.apache.tiles.util.RequestUtils;
+import org.apache.tiles.ComponentDefinition;
+import org.apache.tiles.ComponentDefinitions;
+import org.apache.tiles.DefinitionsFactory;
+import org.apache.tiles.DefinitionsFactoryException;
+import org.apache.tiles.DefinitionsReader;
+import org.apache.tiles.ReloadableDefinitionsFactory;
+import org.apache.tiles.digester.DigesterDefinitionsReader;
+
+/**
+ * {@link org.apache.tiles.DefinitionsFactory DefinitionsFactory} implementation
+ * that manages ComponentDefinitions configuration data from URLs.
+ *
+ * <p>The ComponentDefinition objects are read from the 
+ * {@link org.apache.tiles.digester.DigesterDefinitionsReader DigesterDefinitionsReader}
+ * class unless another implementation is specified.</p>
+ *
+ * @version $Rev$ $Date$ 
+ */
+public class UrlDefinitionsFactory 
+        implements DefinitionsFactory, ReloadableDefinitionsFactory {
+
+    /**
+     * Contains the URL objects identifying where configuration data is found.
+     */
+    private List sources;
+    /**
+     * Reader used to get definitions from the sources.
+     */
+    private DefinitionsReader reader;
+    /**
+     * Contains the dates that the URL sources were last modified.
+     */
+    private Map lastModifiedDates;
+    /**
+     * Contains a list of locales that have been processed.
+     */
+    private List processedLocales;
+    
+    /**
+     * ComponentDefinitions class name.
+     */
+    private String definitionsClassName;
+    
+    /** Creates a new instance of UrlDefinitionsFactory */
+    public UrlDefinitionsFactory() {
+        sources = new ArrayList();
+	lastModifiedDates = new HashMap();
+        processedLocales = new ArrayList();
+    }
+
+    /**
+     * Initializes the DefinitionsFactory and its subcomponents.
+     * 
+     * Implementations may support configuration properties to be passed in via
+     * the params Map.
+     * 
+     * @param params The Map of configuration properties.
+     * @throws DefinitionsFactoryException if an initialization error occurs.
+     */
+    public void init(Map params) throws DefinitionsFactoryException {
+        if (params != null) {
+            String readerClassName = (String) params.get(
+                    DefinitionsFactory.READER_IMPL_PROPERTY);
+            if (readerClassName != null) {
+                try {
+                    Class readerClass = 
+                        RequestUtils.applicationClass(readerClassName);
+                    reader = (DefinitionsReader) readerClass.newInstance();
+                } catch (ClassNotFoundException e) {
+                    throw new DefinitionsFactoryException(
+                            "Cannot find reader class.", e);
+                } catch (InstantiationException e) {
+                    throw new DefinitionsFactoryException(
+                            "Unable to instantiate reader class.", e);
+                } catch (IllegalAccessException e) {
+                    throw new DefinitionsFactoryException(
+                            "Unable to access reader class.", e);
+                }
+            }
+
+            definitionsClassName = (String) params.get(
+                    DefinitionsFactory.DEFINITIONS_IMPL_PROPERTY);
+            if (definitionsClassName != null) {
+                // Attempt to create it just to verify.
+                ComponentDefinitions definitions = createDefinitionsImpl(
+                        definitionsClassName);
+            }
+        }
+        
+        if (reader == null) {
+            reader = new DigesterDefinitionsReader();
+        }
+        
+        reader.init(params);
+    }
+
+    /**
+     * Adds a source where ComponentDefinition objects are stored.
+     * 
+     * Implementations should publish what type of source object they expect.
+     * The source should contain enough information to resolve a configuration
+     * source containing definitions.  The source should be a "base" source for
+     * configurations.  Internationalization and Localization properties will be
+     * applied by implementations to discriminate the correct data sources based
+     * on locale.
+     * 
+     * @param source The configuration source for definitions.
+     * @throws DefinitionsFactoryException if an invalid source is passed in or
+     *      an error occurs resolving the source to an actual data store.
+     */
+    public void addSource(Object source) throws DefinitionsFactoryException {
+        if (source == null) {
+            throw new DefinitionsFactoryException(
+                    "Source object must not be null");
+        }
+        
+        if (!(source instanceof URL)) {
+            throw new DefinitionsFactoryException(
+                    "Source object must be an URL");
+        }
+        
+        sources.add(source);
+    }
+
+    /**
+     * Appends locale-specific {@link ComponentDefinition} objects to an existing
+     * {@link ComponentDefinitions} set by reading locale-specific versions of
+     * the applied sources.
+     * 
+     * @param definitions The ComponentDefinitions object to append to.
+     * @param locale The requested locale.
+     * @throws DefinitionsFactoryException if an error occurs reading definitions.
+     */
+    public void addDefinitions(ComponentDefinitions definitions, Locale locale) 
+            throws DefinitionsFactoryException {
+        
+        List postfixes = calculatePostixes(locale);
+        
+	if (isLocaleProcessed(locale)) {
+	    return;
+	} else {
+	    processedLocales.add(locale);
+	}
+
+        for (int i = 0; i < sources.size(); i++) {
+            URL url = (URL) sources.get(i);
+            String path = url.toExternalForm();
+
+            for (int j = 0; j < postfixes.size(); j++) {
+                String newPath = concatPostfix(path, (String) postfixes.get(j));
+                try {
+                    URL newUrl = new URL(newPath);
+                    URLConnection connection = newUrl.openConnection();
+                    connection.connect();
+		    lastModifiedDates.put(newUrl.toExternalForm(), 
+				new Long(connection.getLastModified()));
+                    Map defsMap = reader.read(connection.getInputStream());
+                    definitions.addDefinitions(defsMap, locale);
+                } catch (FileNotFoundException e) {
+                    // File not found. continue.
+                } catch (IOException e) {
+                    throw new DefinitionsFactoryException(
+                            "I/O error processing configuration.");
+                }
+            }
+        }
+    }
+
+    /**
+     * Creates and returns a {@link ComponentDefinitions} set by reading 
+     * configuration data from the applied sources.
+     * 
+     * @throws DefinitionsFactoryException if an error occurs reading the 
+     *      sources.
+     */
+    public ComponentDefinitions readDefinitions() 
+            throws DefinitionsFactoryException {
+        
+        ComponentDefinitions definitions = 
+                createDefinitionsImpl(definitionsClassName);
+        try {
+            for (int i = 0; i < sources.size(); i++) {
+                URL source = (URL) sources.get(i);
+                URLConnection connection = source.openConnection();
+                connection.connect();
+		lastModifiedDates.put(source.toExternalForm(), 
+				new Long(connection.getLastModified()));
+                Map defsMap = reader.read(connection.getInputStream());
+                definitions.addDefinitions(defsMap);
+            }
+        } catch (IOException e) {
+            throw new DefinitionsFactoryException("I/O error accessing source.", e);
+        }
+        return definitions;
+    }
+    
+    /**
+     * Indicates whether a given locale has been processed or not.
+     * 
+     * This method can be used to avoid unnecessary synchronization of the
+     * DefinitionsFactory in multi-threaded situations.  Check the return of
+     * isLoacaleProcessed before synchronizing the object and reading 
+     * locale-specific definitions.
+     *
+     * @param locale The locale to check.
+     * @return true if the given lcoale has been processed and false otherwise.
+     */
+    public boolean isLocaleProcessed(Locale locale) {
+	if (processedLocales.contains(locale)) {
+	    return true;
+	} else {
+	    return false;
+	}
+    }
+
+    /**
+     * Concat postfix to the name. Take care of existing filename extension.
+     * Transform the given name "name.ext" to have "name" + "postfix" + "ext".
+     * If there is no ext, return "name" + "postfix".
+     * @param name Filename.
+     * @param postfix Postfix to add.
+     * @return Concatenated filename.
+     */
+    private String concatPostfix(String name, String postfix) {
+        if (postfix == null) {
+            return name;
+        }
+
+        // Search file name extension.
+        // take care of Unix files starting with .
+        int dotIndex = name.lastIndexOf(".");
+        int lastNameStart = name.lastIndexOf(java.io.File.pathSeparator);
+        if (dotIndex < 1 || dotIndex < lastNameStart) {
+            return name + postfix;
+        }
+
+        String ext = name.substring(dotIndex);
+        name = name.substring(0, dotIndex);
+        return name + postfix + ext;
+    }
+    
+    /**
+     * Calculate the postixes along the search path from the base bundle to the
+     * bundle specified by baseName and locale.
+     * Method copied from java.util.ResourceBundle
+     * @param locale the locale
+     */
+    private static List calculatePostixes(Locale locale) {
+        final List result = new ArrayList();
+        final String language = locale.getLanguage();
+        final int languageLength = language.length();
+        final String country = locale.getCountry();
+        final int countryLength = country.length();
+        final String variant = locale.getVariant();
+        final int variantLength = variant.length();
+
+        if (languageLength + countryLength + variantLength == 0) {
+            //The locale is "", "", "".
+            return result;
+        }
+
+        final StringBuffer temp = new StringBuffer();
+        temp.append('_');
+        temp.append(language);
+
+        if (languageLength > 0)
+            result.add(temp.toString());
+
+        if (countryLength + variantLength == 0)
+            return result;
+
+        temp.append('_');
+        temp.append(country);
+
+        if (countryLength > 0)
+            result.add(temp.toString());
+
+        if (variantLength == 0) {
+            return result;
+        } else {
+            temp.append('_');
+            temp.append(variant);
+            result.add(temp.toString());
+            return result;
+        }
+    }
+
+    /** 
+     * Creates the ComponentDefinitions instance specified by the initialization
+     * parameter or the default if none is specified.
+     *
+     * @param classname The class of the ComponentDefinitins to create.
+     * @return the instantiated ComponentDefinitions object.
+     * @throws DefinitionsFactoryException if a problem occurs.
+     */
+    protected ComponentDefinitions createDefinitionsImpl(String classname) 
+            throws DefinitionsFactoryException {
+        
+        ComponentDefinitions definitions = null;
+        if (classname != null) {
+            try {
+                Class defsClass = 
+                    RequestUtils.applicationClass(classname);
+                definitions = (ComponentDefinitions) defsClass.newInstance();
+            } catch (ClassNotFoundException e) {
+                throw new DefinitionsFactoryException(
+                        "Cannot find definitions class.", e);
+            } catch (InstantiationException e) {
+                throw new DefinitionsFactoryException(
+                        "Unable to instantiate definitions class.", e);
+            } catch (IllegalAccessException e) {
+                throw new DefinitionsFactoryException(
+                        "Unable to access definitions class.", e);
+            }
+        }
+        
+        if (definitions == null) {
+            definitions = new ComponentDefinitionsImpl();
+        }
+        
+        return definitions;
+    }
+
+    /**
+     * Indicates whether the DefinitionsFactory is out of date and needs to be
+     * reloaded.
+     */
+    public boolean refreshRequired() {
+	boolean status = false;
+
+	Set urls = lastModifiedDates.keySet();
+
+	try {
+	    Iterator i = urls.iterator();
+	    while (i.hasNext()) {
+		String urlPath = (String) i.next();
+		Long lastModifiedDate = (Long) lastModifiedDates.get(urlPath);
+		URL url = new URL(urlPath);
+		URLConnection connection = url.openConnection();
+		connection.connect();
+		long newModDate = connection.getLastModified();
+		if (newModDate != lastModifiedDate.longValue()) {
+		    status = true;
+		    break;
+		}
+	    }
+	} catch (Exception e) {
+            // Should probably log here.
+            return true;
+	}
+	return status;
+    }
+    
+}

Added: struts/sandbox/trunk/tiles/src/java/org/apache/tiles/digester/DigesterDefinitionsReader.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/tiles/src/java/org/apache/tiles/digester/DigesterDefinitionsReader.java?rev=234416&view=auto
==============================================================================
--- struts/sandbox/trunk/tiles/src/java/org/apache/tiles/digester/DigesterDefinitionsReader.java (added)
+++ struts/sandbox/trunk/tiles/src/java/org/apache/tiles/digester/DigesterDefinitionsReader.java Sun Aug 21 19:23:25 2005
@@ -0,0 +1,365 @@
+/*
+ * $Id$
+ *
+ * Copyright 1999-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.tiles.digester;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.commons.digester.Digester;
+import org.apache.tiles.ComponentDefinition;
+import org.apache.tiles.DefinitionsFactoryException;
+import org.apache.tiles.DefinitionsReader;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * Reads {@link org.apache.tiles.ComponentDefinition ComponentDefinition} objects
+ * from an XML InputStream using Digester.
+ *
+ * <p>This <code>DefinitionsReader</code> implementation expects the source to be 
+ * passed as an <code>InputStream</code>.  It parses XML data from the source and
+ * builds a Map of ComponentDefinition objects.</p>
+ *
+ * <p>The Digester object can be configured by passing in initialization parameters.
+ * Currently the only parameter that is supported is the <code>validating</code>
+ * parameter.  This value is set to <code>false</code> by default.  To enable DTD
+ * validation for XML ComponentDefinition files, give the init method a parameter
+ * with a key of <code>definitions-parser-validate</code> and a value of 
+ * <code>&quot;true&quot;</code>.
+ *
+ * <p>The ComponentDefinition objects are stored internally in a Map.  The Map is
+ * stored as an instance variable rather than a local variable in the <code>read</code>
+ * method.  This means that instances of this class are <strong>not</strong> 
+ * thread-safe and access by multiple threads must be synchronized.</p>
+ *
+ * @version $Rev$ $Date$ 
+ */
+public class DigesterDefinitionsReader implements DefinitionsReader {
+    
+    /** 
+     * Digester validation parameter name. 
+     */
+    public static final String PARSER_VALIDATE_PARAMETER_NAME =
+        "definitions-parser-validate";
+    /**
+     * <code>Digester</code> object used to read ComponentDefinition data
+     * from the source.
+     */
+    protected Digester digester;
+    /**
+     * Stores ComponentDefinition objects.
+     */
+    Map definitions;
+    /**
+     * Should we use a validating XML parser to read the configuration file.
+     * Default is <code>false</code>.
+     */
+    protected boolean validating = false;
+    /**
+     * The set of public identifiers, and corresponding resource names for
+     * the versions of the configuration file DTDs we know about.  There
+     * <strong>MUST</strong> be an even number of Strings in this list!
+     */
+    protected String registrations[] = {
+        "-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN",
+        "/org/apache/tiles/resources/tiles-config_1_1.dtd",
+        "-//Apache Software Foundation//DTD Tiles Configuration 1.2//EN",
+        "/org/apache/tiles/resources/tiles-config_1_2.dtd",
+    };
+    
+    /**
+     * Indicates whether init method has been called.
+     */
+    private boolean inited = false;
+
+    /** Creates a new instance of DigesterDefinitionsReader */
+    public DigesterDefinitionsReader() {
+        digester = new Digester();
+        digester.setValidating(validating);
+        digester.setNamespaceAware(true);
+        digester.setUseContextClassLoader(true);
+
+        // Register our local copy of the DTDs that we can find
+        for (int i = 0; i < registrations.length; i += 2) {
+            URL url = this.getClass().getClassLoader().getResource(
+                    registrations[i+1]);
+            if (url != null) {
+                digester.register(registrations[i], url.toString());
+            }
+        }
+    }
+
+    /**
+     * Reads <code>{@link ComponentDefinition}</code> objects from a source.
+     * 
+     * Implementations should publish what type of source object is expected.
+     * 
+     * @param source The <code>InputStream</code> source from which definitions 
+     *  will be read.
+     * @return a Map of <code>ComponentDefinition</code> objects read from
+     *  the source.
+     * @throws DefinitionsFactoryException if the source is invalid or
+     *  an error occurs when reading definitions.
+     */
+    public Map read(Object source) throws DefinitionsFactoryException {
+        
+        // Get out if we have not been initialized.
+        if (!inited) {
+            throw new DefinitionsFactoryException(
+                    "Definitions reader has not been initialized.");
+        }
+        
+        // This is an instance variable instead of a local variable because
+        // we want to be able to call the addDefinition method to populate it.
+        // But we reset the Map here, which, of course, has threading implications.
+        definitions = new HashMap();
+        
+        if (source == null) {
+            // Perhaps we should throw an exception here.
+            return null;
+        }
+        
+        InputStream input = null;
+        try {
+            input = (InputStream) source;
+        } catch (ClassCastException e) {
+            throw new DefinitionsFactoryException(
+                    "Invalid source type.  Requires java.io.InputStream.", e);
+        }
+        
+        try {
+            // set first object in stack
+            //digester.clear();
+            digester.push(this);
+            // parse
+            digester.parse(input);
+            
+        } catch (SAXException e) {
+            throw new DefinitionsFactoryException(
+                    "XML error reading definitions.", e);
+        } catch (IOException e) {
+            throw new DefinitionsFactoryException(
+                    "I/O Error reading definitions.", e);
+        }
+        
+        return definitions;
+    }
+
+    /**
+     * Initializes the <code>DefinitionsReader</code> object.
+     * 
+     * This method must be called before the {@link #read} method is called.
+     * 
+     * @param params A map of properties used to set up the reader.
+     * @throws DefinitionsFactoryException if required properties are not
+     *  passed in or the initialization fails.
+     */
+    public void init(Map params) throws DefinitionsFactoryException {
+        
+        if (params != null) {
+            String value = (String) params.get(PARSER_VALIDATE_PARAMETER_NAME);
+            if (value != null) {
+                digester.setValidating(Boolean.valueOf(value).booleanValue());
+            }
+        }
+
+        
+        initDigesterForTilesDefinitionsSyntax( digester );
+        initDigesterForComponentsDefinitionsSyntax( digester );
+        initDigesterForInstancesSyntax( digester );
+        
+        inited = true;
+    }
+    
+    
+    /**
+     * Init digester for components syntax.
+     * This is an old set of rules, left for backward compatibility.
+     * @param digester Digester instance to use.
+     */
+    private void initDigesterForComponentsDefinitionsSyntax( Digester digester ) {
+        // Common constants
+        String PACKAGE_NAME = "org.apache.tiles.xmlDefinition";
+        String DEFINITION_TAG = "component-definitions/definition";
+        String definitionHandlerClass = PACKAGE_NAME + ".XmlDefinition";
+
+        String PUT_TAG  = DEFINITION_TAG + "/put";
+        String putAttributeHandlerClass = PACKAGE_NAME + ".XmlAttribute";
+
+        String LIST_TAG = DEFINITION_TAG + "/putList";
+        String listHandlerClass     = PACKAGE_NAME + ".XmlListAttribute";
+
+        String ADD_LIST_ELE_TAG = LIST_TAG + "/add";
+
+        // syntax rules
+        digester.addObjectCreate(  DEFINITION_TAG, definitionHandlerClass );
+        digester.addSetProperties( DEFINITION_TAG);
+        digester.addSetNext(       DEFINITION_TAG, "addDefinition", definitionHandlerClass);
+        // put / putAttribute rules
+        digester.addObjectCreate(  PUT_TAG, putAttributeHandlerClass);
+        digester.addSetNext(       PUT_TAG, "addAttribute", putAttributeHandlerClass);
+        digester.addSetProperties( PUT_TAG);
+        digester.addCallMethod(    PUT_TAG, "setBody", 0);
+        // list rules
+        digester.addObjectCreate(  LIST_TAG, listHandlerClass);
+        digester.addSetProperties( LIST_TAG);
+        digester.addSetNext(       LIST_TAG, "addAttribute", putAttributeHandlerClass);
+        // list elements rules
+        // We use Attribute class to avoid rewriting a new class.
+        // Name part can't be used in listElement attribute.
+        digester.addObjectCreate(  ADD_LIST_ELE_TAG, putAttributeHandlerClass);
+        digester.addSetNext(       ADD_LIST_ELE_TAG, "add", putAttributeHandlerClass);
+        digester.addSetProperties( ADD_LIST_ELE_TAG);
+        digester.addCallMethod(    ADD_LIST_ELE_TAG, "setBody", 0);
+    }
+
+    /**
+     * Init digester for Tiles syntax.
+     * Same as components, but with first element = tiles-definitions
+     * @param digester Digester instance to use.
+     */
+    private void initDigesterForTilesDefinitionsSyntax( Digester digester ) {
+        // Common constants
+        String PACKAGE_NAME = "org.apache.tiles.xmlDefinition";
+        String DEFINITION_TAG = "tiles-definitions/definition";
+        String definitionHandlerClass = PACKAGE_NAME + ".XmlDefinition";
+
+        String PUT_TAG  = DEFINITION_TAG + "/put";
+        String putAttributeHandlerClass = PACKAGE_NAME + ".XmlAttribute";
+
+        //String LIST_TAG = DEFINITION_TAG + "/putList";
+        // List tag value
+        String LIST_TAG = "putList";
+        String DEF_LIST_TAG = DEFINITION_TAG + "/" + LIST_TAG;
+        String listHandlerClass     = PACKAGE_NAME + ".XmlListAttribute";
+        // Tag value for adding an element in a list
+        String ADD_LIST_ELE_TAG = "*/" + LIST_TAG + "/add";
+
+        // syntax rules
+        digester.addObjectCreate(  DEFINITION_TAG, definitionHandlerClass );
+        digester.addSetProperties( DEFINITION_TAG);
+        digester.addSetNext(       DEFINITION_TAG, "addDefinition", definitionHandlerClass);
+        // put / putAttribute rules
+        // Rules for a same pattern are called in order, but rule.end() are called
+        // in reverse order.
+        // SetNext and CallMethod use rule.end() method. So, placing SetNext in
+        // first position ensure it will be called last (sic).
+        digester.addObjectCreate(  PUT_TAG, putAttributeHandlerClass);
+        digester.addSetNext(       PUT_TAG, "addAttribute", putAttributeHandlerClass);
+        digester.addSetProperties( PUT_TAG);
+        digester.addCallMethod(    PUT_TAG, "setBody", 0);
+        // Definition level list rules
+        // This is rules for lists nested in a definition
+        digester.addObjectCreate(  DEF_LIST_TAG, listHandlerClass);
+        digester.addSetProperties( DEF_LIST_TAG);
+        digester.addSetNext(       DEF_LIST_TAG, "addAttribute", putAttributeHandlerClass);
+        // list elements rules
+        // We use Attribute class to avoid rewriting a new class.
+        // Name part can't be used in listElement attribute.
+        digester.addObjectCreate(  ADD_LIST_ELE_TAG, putAttributeHandlerClass);
+        digester.addSetNext(       ADD_LIST_ELE_TAG, "add", putAttributeHandlerClass);
+        digester.addSetProperties( ADD_LIST_ELE_TAG);
+        digester.addCallMethod(    ADD_LIST_ELE_TAG, "setBody", 0);
+
+        // nested list elements rules
+        // Create a list handler, and add it to parent list
+        String NESTED_LIST = "*/" + LIST_TAG + "/" + LIST_TAG;
+        digester.addObjectCreate(  NESTED_LIST, listHandlerClass);
+        digester.addSetProperties( NESTED_LIST);
+        digester.addSetNext(       NESTED_LIST, "add", putAttributeHandlerClass);
+
+        // item elements rules
+        // We use Attribute class to avoid rewriting a new class.
+        // Name part can't be used in listElement attribute.
+        //String ADD_WILDCARD = LIST_TAG + "/addItem";
+        // non String ADD_WILDCARD = LIST_TAG + "/addx*";
+        String ADD_WILDCARD = "*/item";
+        String menuItemDefaultClass = "org.apache.tiles.beans.SimpleMenuItem";
+        digester.addObjectCreate(  ADD_WILDCARD, menuItemDefaultClass, "classtype");
+        digester.addSetNext(       ADD_WILDCARD, "add", "java.lang.Object");
+        digester.addSetProperties( ADD_WILDCARD);
+
+        // bean elements rules
+        String BEAN_TAG = "*/bean";
+        String beanDefaultClass = "org.apache.tiles.beans.SimpleMenuItem";
+        digester.addObjectCreate(  BEAN_TAG, beanDefaultClass, "classtype");
+        digester.addSetNext(       BEAN_TAG, "add", "java.lang.Object");
+        digester.addSetProperties( BEAN_TAG);
+
+        // Set properties to surrounding element
+        digester.addSetProperty(BEAN_TAG+ "/set-property", "property", "value");
+    }
+
+    /**
+     * Init digester in order to parse instances definition file syntax.
+     * Instances is an old name for "definition". This method is left for
+     * backwards compatibility.
+     * @param digester Digester instance to use.
+     */
+    private void initDigesterForInstancesSyntax( Digester digester ) {
+        // Build a digester to process our configuration resource
+        String PACKAGE_NAME = "org.apache.tiles.xmlDefinition";
+        String INSTANCE_TAG = "component-instances/instance";
+        String instanceHandlerClass = PACKAGE_NAME + ".XmlDefinition";
+
+        String PUT_TAG = INSTANCE_TAG + "/put";
+        String PUTATTRIBUTE_TAG = INSTANCE_TAG + "/putAttribute";
+        String putAttributeHandlerClass = PACKAGE_NAME + ".XmlAttribute";
+
+        String LIST_TAG     = INSTANCE_TAG + "/putList";
+        String listHandlerClass     = PACKAGE_NAME + ".XmlListAttribute";
+
+        String ADD_LIST_ELE_TAG = LIST_TAG + "/add";
+
+        // component instance rules
+        digester.addObjectCreate(  INSTANCE_TAG, instanceHandlerClass );
+        digester.addSetProperties( INSTANCE_TAG);
+        digester.addSetNext(       INSTANCE_TAG, "addDefinition", instanceHandlerClass);
+        // put / putAttribute rules
+        digester.addObjectCreate(  PUTATTRIBUTE_TAG, putAttributeHandlerClass);
+        digester.addSetProperties( PUTATTRIBUTE_TAG);
+        digester.addSetNext(       PUTATTRIBUTE_TAG, "addAttribute", putAttributeHandlerClass);
+        // put / putAttribute rules
+        digester.addObjectCreate(  PUT_TAG, putAttributeHandlerClass);
+        digester.addSetProperties( PUT_TAG);
+        digester.addSetNext(       PUT_TAG, "addAttribute", putAttributeHandlerClass);
+        // list rules
+        digester.addObjectCreate(  LIST_TAG, listHandlerClass);
+        digester.addSetProperties( LIST_TAG);
+        digester.addSetNext(       LIST_TAG, "addAttribute", putAttributeHandlerClass);
+        // list elements rules
+        // We use Attribute class to avoid rewriting a new class.
+        // Name part can't be used in listElement attribute.
+        digester.addObjectCreate(  ADD_LIST_ELE_TAG, putAttributeHandlerClass);
+        digester.addSetProperties( ADD_LIST_ELE_TAG);
+        digester.addSetNext(       ADD_LIST_ELE_TAG, "add", putAttributeHandlerClass);
+    }
+    
+    /**
+     * Adds a new <code>ComponentDefinition</code> to the internal Map or replaces
+     * an existing one.
+     *
+     * @param definition The ComponentDefinition object to be added.
+     */
+    public void addDefinition(ComponentDefinition definition) {
+        definitions.put(definition.getName(), definition);
+    }
+}

Added: struts/sandbox/trunk/tiles/src/java/org/apache/tiles/filter/TilesFilter.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/tiles/src/java/org/apache/tiles/filter/TilesFilter.java?rev=234416&view=auto
==============================================================================
--- struts/sandbox/trunk/tiles/src/java/org/apache/tiles/filter/TilesFilter.java (added)
+++ struts/sandbox/trunk/tiles/src/java/org/apache/tiles/filter/TilesFilter.java Sun Aug 21 19:23:25 2005
@@ -0,0 +1,140 @@
+/*
+ * $Id$
+ *
+ * Copyright 1999-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.tiles.filter;
+
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import org.apache.tiles.ComponentDefinitions;
+import org.apache.tiles.DefinitionsFactory;
+import org.apache.tiles.ReloadableDefinitionsFactory;
+import org.apache.tiles.TilesUtil;
+import org.apache.tiles.TilesUtilImpl;
+
+/**
+ * Processes Reloadable Tiles Definitions.
+ *
+ * @version $Rev$ $Date$ 
+ */
+
+public class TilesFilter implements Filter {
+    
+    /**
+     * The filter configuration object we are associated with.  If
+     * this value is null, this filter instance is not currently
+     * configured.
+     */
+    private FilterConfig filterConfig = null;
+    
+    public TilesFilter() {
+    }
+    
+    /**
+     * Checks whether Tiles Definitions need to be reloaded.
+     *
+     * @param request The servlet request we are processing
+     * @param response The servlet response we are creating
+     * @param chain The filter chain we are processing
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a servlet error occurs
+     */
+    public void doFilter(ServletRequest request, ServletResponse response,
+            FilterChain chain)
+            throws IOException, ServletException {
+        
+        try {
+            DefinitionsFactory factory = TilesUtil.getDefinitionsFactory(request, 
+                    filterConfig.getServletContext());
+            
+            if (factory instanceof ReloadableDefinitionsFactory) {
+                if (((ReloadableDefinitionsFactory) factory).refreshRequired()) {
+                    if (debug) {
+                        log("Updating Tiles definitions.");
+                    }
+                    
+                    ComponentDefinitions newDefs = null;
+                    synchronized (factory) {
+                         newDefs = factory.readDefinitions();
+                    }
+                    
+                    ComponentDefinitions definitions = (ComponentDefinitions)
+                            filterConfig.getServletContext().getAttribute(
+                            TilesUtilImpl.DEFINITIONS_OBJECT);
+                    synchronized (definitions) {
+                        definitions.reset();
+                        definitions.addDefinitions(newDefs.getBaseDefinitions());
+                    }
+                }
+            }
+            
+            chain.doFilter(request, response);
+            
+        } catch(Exception e) {
+            throw new ServletException("Error processing request.", e);
+        }
+    }
+    
+    
+    /**
+     * Return the filter configuration object for this filter.
+     */
+    public FilterConfig getFilterConfig() {
+        return (this.filterConfig);
+    }
+    
+    
+    /**
+     * Set the filter configuration object for this filter.
+     *
+     * @param filterConfig The filter configuration object
+     */
+    public void setFilterConfig(FilterConfig filterConfig) {
+        
+        this.filterConfig = filterConfig;
+    }
+    
+    /**
+     * Destroy method for this filter
+     */
+    public void destroy() {
+    }
+    
+    
+    /**
+     * Init method for this filter
+     */
+    public void init(FilterConfig filterConfig) {
+        this.filterConfig = filterConfig;
+        
+        if (debug) {
+            log("TilesFilter:Initializing filter");
+        }
+    }
+    
+    public void log(String msg) {
+        filterConfig.getServletContext().log(msg);
+    }
+    
+    private static final boolean debug = true;
+}

Modified: struts/sandbox/trunk/tiles/src/java/org/apache/tiles/servlets/TilesServlet.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/tiles/src/java/org/apache/tiles/servlets/TilesServlet.java?rev=234416&r1=234415&r2=234416&view=diff
==============================================================================
--- struts/sandbox/trunk/tiles/src/java/org/apache/tiles/servlets/TilesServlet.java (original)
+++ struts/sandbox/trunk/tiles/src/java/org/apache/tiles/servlets/TilesServlet.java Sun Aug 21 19:23:25 2005
@@ -187,8 +187,6 @@
 	 * <i>definitions-config</i> context param was not
 	 * specified, Tiles assumes that your Tiles definition
 	 * file is <code>/WEB-INF/tiles.xml</code>.
-	 *
-	 * @param config The servlet config
 	 */
 	protected DefinitionsFactoryConfig readFactoryConfig() 
 		throws ServletException {
@@ -243,7 +241,7 @@
 	 * if an exception is thrown when the tiles:insert tag is
 	 * activated.
 	 *
-	 * @param servletContext The servlet context
+	 * @param config The servlet configuration
 	 * @param ex An exception
 	 */
 	private void saveExceptionMessage(ServletConfig config, Exception ex) {

Added: struts/sandbox/trunk/tiles/src/test/org/apache/tiles/TestComponentDefinitions.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/tiles/src/test/org/apache/tiles/TestComponentDefinitions.java?rev=234416&view=auto
==============================================================================
--- struts/sandbox/trunk/tiles/src/test/org/apache/tiles/TestComponentDefinitions.java (added)
+++ struts/sandbox/trunk/tiles/src/test/org/apache/tiles/TestComponentDefinitions.java Sun Aug 21 19:23:25 2005
@@ -0,0 +1,214 @@
+/*
+ * $Id$
+ *
+ * Copyright 1999-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.tiles;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.tiles.definition.ComponentDefinitionsImpl;
+import org.apache.tiles.xmlDefinition.XmlAttribute;
+
+/**
+ * Tests the ComponentDefinitionsImpl class.
+ *
+ * @version $Rev$ $Date$ 
+ */
+public class TestComponentDefinitions extends TestCase {
+    
+    /** Creates a new instance of TestComponentDefinitions */
+    public TestComponentDefinitions(String name) {
+        super(name);
+    }
+    
+    /**
+     * Start the tests.
+     *
+     * @param theArgs the arguments. Not used
+     */
+    public static void main(String[] theArgs) {
+        junit.awtui.TestRunner.main(
+            new String[] { TestComponentDefinitions.class.getName()});
+    }
+
+    /**
+     * @return a test suite (<code>TestSuite</code>) that includes all methods
+     *         starting with "test"
+     */
+    public static Test suite() {
+        return new TestSuite(TestComponentDefinitions.class);
+    }
+
+    /**
+     * Tests the inheritance properties of ComponentDefinition objects.
+     */
+    public void testResolveInheritances() {
+        Map defs = new HashMap();
+        
+        ComponentDefinition def = new ComponentDefinition();
+        def.setName("parent.def1");
+        def.setPath("/test1.jsp");
+        XmlAttribute attr = new XmlAttribute();
+        attr.setName("attr1");
+        attr.setValue("value1");
+        def.addAttribute(attr);
+        defs.put(def.getName(), def);
+        
+        def = new ComponentDefinition();
+        def.setName("child.def1");
+        def.setExtends("parent.def1");
+        attr = new XmlAttribute();
+        attr.setName("attr1");
+        attr.setValue("New value");
+        def.addAttribute(attr);
+        defs.put(def.getName(), def);
+        
+        ComponentDefinitions definitions = new ComponentDefinitionsImpl();
+        try {
+            definitions.addDefinitions(defs);
+        } catch (NoSuchDefinitionException e) {
+            fail("Test failure: " + e);
+        }
+        
+        assertNotNull("Couldn't get parent.", 
+                definitions.getDefinition("parent.def1"));
+        assertEquals("Incorrect path value." , "/test1.jsp",
+                definitions.getDefinition("parent.def1").getPath());
+        assertEquals("Incorrect attr1 value", "value1",
+                definitions.getDefinition("parent.def1").getAttribute("attr1"));
+        
+        assertNotNull("Couldn't get child.", 
+                definitions.getDefinition("child.def1"));
+        assertEquals("Incorrect path value." , "/test1.jsp",
+                definitions.getDefinition("child.def1").getPath());
+        assertEquals("Incorrect attr1 value", "New value",
+                definitions.getDefinition("child.def1").getAttribute("attr1"));
+    }
+    
+    /**
+     * Tests the inheritance with localized definitions.
+     */
+    public void testLocalizedResolveInheritances() {
+        Map defs = new HashMap();
+        ComponentDefinition def = new ComponentDefinition();
+        def.setName("parent.def1");
+        def.setPath("/test1.jsp");
+        XmlAttribute attr = new XmlAttribute();
+        attr.setName("attr1");
+        attr.setValue("value1");
+        def.addAttribute(attr);
+        defs.put(def.getName(), def);
+        
+        def = new ComponentDefinition();
+        def.setName("child.def1");
+        def.setExtends("parent.def1");
+        attr = new XmlAttribute();
+        attr.setName("attr1");
+        attr.setValue("New value");
+        def.addAttribute(attr);
+        defs.put(def.getName(), def);
+
+        Map localDefs = new HashMap();
+        def = new ComponentDefinition();
+        def.setName("child.def1");
+        def.setExtends("parent.def1");
+        attr = new XmlAttribute();
+        attr.setName("attr1");
+        attr.setValue("US Value");
+        def.addAttribute(attr);
+        localDefs.put(def.getName(), def);
+
+        ComponentDefinitions definitions = new ComponentDefinitionsImpl();
+        try {
+            definitions.addDefinitions(defs);
+            definitions.addDefinitions(localDefs, Locale.US);
+        } catch (NoSuchDefinitionException e) {
+            fail("Test failure: " + e);
+        }
+        
+        assertNotNull("Couldn't get parent.", 
+                definitions.getDefinition("parent.def1"));
+        assertEquals("Incorrect path value." , "/test1.jsp",
+                definitions.getDefinition("parent.def1").getPath());
+        assertEquals("Incorrect attr1 value", "value1",
+                definitions.getDefinition("parent.def1").getAttribute("attr1"));
+        
+        assertNotNull("Couldn't get child.", 
+                definitions.getDefinition("child.def1"));
+        assertEquals("Incorrect path value." , "/test1.jsp",
+                definitions.getDefinition("child.def1").getPath());
+        assertEquals("Incorrect attr1 value", "New value",
+                definitions.getDefinition("child.def1").getAttribute("attr1"));
+        
+        assertNotNull("Couldn't get parent.", 
+                definitions.getDefinition("parent.def1", Locale.US));
+        assertEquals("Incorrect path value." , "/test1.jsp",
+                definitions.getDefinition("parent.def1", Locale.US).getPath());
+        assertEquals("Incorrect attr1 value", "value1",
+                definitions.getDefinition("parent.def1", Locale.US).getAttribute("attr1"));
+        
+        assertNotNull("Couldn't get child.", 
+                definitions.getDefinition("child.def1", Locale.US));
+        assertEquals("Incorrect path value." , "/test1.jsp",
+                definitions.getDefinition("child.def1", Locale.US).getPath());
+        assertEquals("Incorrect attr1 value", "US Value",
+                definitions.getDefinition("child.def1", Locale.US).getAttribute("attr1"));
+    }
+    
+    /**
+     * Tests the reset method.
+     */
+    public void testReset() {
+        Map defs = new HashMap();
+        
+        ComponentDefinition def = new ComponentDefinition();
+        def.setName("parent.def1");
+        def.setPath("/test1.jsp");
+        XmlAttribute attr = new XmlAttribute();
+        attr.setName("attr1");
+        attr.setValue("value1");
+        def.addAttribute(attr);
+        defs.put(def.getName(), def);
+        
+        def = new ComponentDefinition();
+        def.setName("child.def1");
+        def.setExtends("parent.def1");
+        attr = new XmlAttribute();
+        attr.setName("attr1");
+        attr.setValue("New value");
+        def.addAttribute(attr);
+        defs.put(def.getName(), def);
+        
+        ComponentDefinitions definitions = new ComponentDefinitionsImpl();
+        try {
+            definitions.addDefinitions(defs);
+        } catch (NoSuchDefinitionException e) {
+            fail("Test failure: " + e);
+        }
+        
+        assertNotNull("Couldn't get parent.", 
+                definitions.getDefinition("parent.def1"));
+        
+        definitions.reset();
+        assertNull("Definitions should be null.", 
+                definitions.getDefinition("parent.def1"));
+    }
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org