You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2004/11/03 18:28:17 UTC

cvs commit: jakarta-tapestry/eclipse Tapestry-Workbench.launch

hlship      2004/11/03 09:28:16

  Modified:    framework/src/java/org/apache/tapestry/enhance
                        EnhancementOperation.java
                        SpecifiedPropertyWorker.java
                        ParameterPropertyWorker.java
                        EnhancementOperationImpl.java EnhanceUtils.java
               framework/src/test/org/apache/tapestry/enhance
                        TestEnhancementOperation.java
               framework/src/descriptor/META-INF tapestry.enhance.xml
               src/documentation/content/xdocs/UsersGuide state.xml
               examples/Workbench/src/context/WEB-INF Chart.page Border.jwc
               framework/src/java/org/apache/tapestry/html
                        ExceptionDisplay.java
               framework/src/java/org/apache/tapestry
                        AbstractComponent.java
               eclipse  Tapestry-Workbench.launch
  Added:       framework/src/java/org/apache/tapestry/enhance
                        AbstractPropertyWorker.java
               framework/src/test/org/apache/tapestry/enhance
                        TestAbstractPropertyWorker.java
  Log:
  Modify the component enhancers to support creating concrete properties for other-wise unclaimed abstract properties.
  
  Revision  Changes    Path
  1.6       +28 -0     jakarta-tapestry/framework/src/java/org/apache/tapestry/enhance/EnhancementOperation.java
  
  Index: EnhancementOperation.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/enhance/EnhancementOperation.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- EnhancementOperation.java	2 Nov 2004 13:30:00 -0000	1.5
  +++ EnhancementOperation.java	3 Nov 2004 17:28:16 -0000	1.6
  @@ -14,6 +14,9 @@
   
   package org.apache.tapestry.enhance;
   
  +import java.util.List;
  +
  +import org.apache.hivemind.service.BodyBuilder;
   import org.apache.hivemind.service.MethodSignature;
   import org.apache.tapestry.spec.IComponentSpecification;
   
  @@ -40,6 +43,13 @@
       public void claimProperty(String propertyName);
   
       /**
  +     * Returns a list of the names of existing properties that are not claimed and which have
  +     * abstract accessor methods.
  +     */
  +
  +    public List findUnclaimedAbstractProperties();
  +
  +    /**
        * Adds a field to the enhanced class; the field will be private and use the provided name and
        * type.
        */
  @@ -120,4 +130,22 @@
        */
   
       public Class getPropertyType(String name);
  +
  +    /**
  +     * Allows for a kind of distributed construction of a particular method, within a particular
  +     * interface. The {@link BodyBuilder}for a given interface method can be obtained and added to.
  +     * When the enhanced class is finialized, the method is added with whatever contents are in its
  +     * body.  If the base class implements the method, then the method body will
  +     * include an initial call to that implementation.
  +     * 
  +     * <p>
  +     * At this time, this works best for void methods.
  +     * 
  +     * @param interfaceClass the interface containing the method. If the base class does
  +     *  not implement the interface, then the enhanced class will have the interface
  +     * added.
  +     * @param methodSignature the signature of the method to be added.
  +     * @returns The {@link BodyBuilder} for the specified method.
  +     */
  +    public BodyBuilder getBodyBuilderForMethod(Class interfaceClass, MethodSignature methodSignature);
   }
  
  
  
  1.4       +7 -11     jakarta-tapestry/framework/src/java/org/apache/tapestry/enhance/SpecifiedPropertyWorker.java
  
  Index: SpecifiedPropertyWorker.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/enhance/SpecifiedPropertyWorker.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- SpecifiedPropertyWorker.java	3 Nov 2004 13:53:43 -0000	1.3
  +++ SpecifiedPropertyWorker.java	3 Nov 2004 17:28:16 -0000	1.4
  @@ -87,9 +87,14 @@
           // Release 3.0 would squack a bit about overriding non-abstract methods
           // if they exist. 3.1 is less picky ... it blindly adds new methods, possibly
           // overwriting methods in the base component class.
  -
  -        addAccessor(op, propertyName, propertyType, field);
  +        
  +        EnhanceUtils.createSimpleAccessor(op, field, propertyName, propertyType);
  +        
           addMutator(op, propertyName, propertyType, field, ps.isPersistent());
  +        
  +        // TODO: For properties with no initializer, should use the
  +        // same kind of logic as AbstractPropertyWorker (and remove some logic
  +        // from PageLoader.
       }
   
       // Package private for testing purposes
  @@ -108,15 +113,6 @@
           Class propertyType = op.getPropertyType(propertyName);
   
           return propertyType == null ? Object.class : propertyType;
  -    }
  -
  -    private void addAccessor(EnhancementOperation op, String name, Class type, String field)
  -    {
  -        String methodName = op.getAccessorMethodName(name);
  -
  -        MethodSignature sig = new MethodSignature(type, methodName, null, null);
  -
  -        op.addMethod(Modifier.PUBLIC, sig, "return " + field + ";");
       }
   
       private void addMutator(EnhancementOperation op, String propertyName, Class propertyType,
  
  
  
  1.4       +1 -32     jakarta-tapestry/framework/src/java/org/apache/tapestry/enhance/ParameterPropertyWorker.java
  
  Index: ParameterPropertyWorker.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/enhance/ParameterPropertyWorker.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- ParameterPropertyWorker.java	2 Nov 2004 18:59:14 -0000	1.3
  +++ ParameterPropertyWorker.java	3 Nov 2004 17:28:16 -0000	1.4
  @@ -96,38 +96,7 @@
               return;
           }
   
  -        createSimpleParameterProperty(op, propertyName, propertyType);
  -    }
  -
  -    private void createSimpleParameterProperty(EnhancementOperation op, String propertyName,
  -            Class propertyType)
  -    {
  -        String fieldName = "_$" + propertyName;
  -
  -        op.addField(fieldName, propertyType);
  -
  -        createSimpleAccessor(op, fieldName, propertyName, propertyType);
  -        createSimpleMutator(op, fieldName, propertyName, propertyType);
  -    }
  -
  -    private void createSimpleAccessor(EnhancementOperation op, String fieldName,
  -            String propertyName, Class propertyType)
  -    {
  -        String methodName = op.getAccessorMethodName(propertyName);
  -
  -        op.addMethod(
  -                Modifier.PUBLIC,
  -                new MethodSignature(propertyType, methodName, null, null),
  -                "return " + fieldName + ";");
  -    }
  -
  -    private void createSimpleMutator(EnhancementOperation op, String fieldName,
  -            String propertyName, Class propertyType)
  -    {
  -        String methodName = EnhanceUtils.createMutatorMethodName(propertyName);
  -
  -        op.addMethod(Modifier.PUBLIC, new MethodSignature(void.class, methodName, new Class[]
  -        { propertyType }, null), fieldName + " = $1;");
  +        EnhanceUtils.createSimpleProperty(op, propertyName, propertyType);
       }
   
       private void createAutoParameterProperty(EnhancementOperation op, String parameterName,
  
  
  
  1.7       +136 -0    jakarta-tapestry/framework/src/java/org/apache/tapestry/enhance/EnhancementOperationImpl.java
  
  Index: EnhancementOperationImpl.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/enhance/EnhancementOperationImpl.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- EnhancementOperationImpl.java	2 Nov 2004 18:59:14 -0000	1.6
  +++ EnhancementOperationImpl.java	3 Nov 2004 17:28:16 -0000	1.7
  @@ -19,9 +19,12 @@
   import java.beans.Introspector;
   import java.beans.PropertyDescriptor;
   import java.lang.reflect.Constructor;
  +import java.lang.reflect.Method;
  +import java.lang.reflect.Modifier;
   import java.util.ArrayList;
   import java.util.HashMap;
   import java.util.HashSet;
  +import java.util.Iterator;
   import java.util.List;
   import java.util.Map;
   import java.util.Set;
  @@ -34,6 +37,7 @@
   import org.apache.hivemind.service.ClassFab;
   import org.apache.hivemind.service.ClassFactory;
   import org.apache.hivemind.service.MethodSignature;
  +import org.apache.hivemind.util.ToStringBuilder;
   import org.apache.tapestry.services.ComponentConstructor;
   import org.apache.tapestry.spec.IComponentSpecification;
   
  @@ -65,6 +69,18 @@
       private List _constructorArguments = new ArrayList();
   
       /**
  +     * Set of interfaces added to the enhanced class.
  +     */
  +
  +    private Set _addedInterfaces = new HashSet();
  +
  +    /**
  +     * Map of {@link BodyBuilder}, keyed on {@link MethodSignature}.
  +     */
  +
  +    private Map _incompleteMethods = new HashMap();
  +
  +    /**
        * Keyed on class to instance variable name.
        */
   
  @@ -98,6 +114,17 @@
           introspectBaseClass();
       }
   
  +    public String toString()
  +    {
  +        ToStringBuilder builder = new ToStringBuilder(this);
  +
  +        builder.append("baseClass", _baseClass.getName());
  +        builder.append("claimedProperties", _claimedProperties);
  +        builder.append("classFab", _classFab);
  +
  +        return builder.toString();
  +    }
  +
       private void introspectBaseClass()
       {
           try
  @@ -312,6 +339,8 @@
   
       private void finalizeEnhancedClass()
       {
  +        finalizeIncompleteMethods();
  +
           if (_classFab == null)
               return;
   
  @@ -326,6 +355,24 @@
           }
       }
   
  +    private void finalizeIncompleteMethods()
  +    {
  +        Iterator i = _incompleteMethods.entrySet().iterator();
  +        while (i.hasNext())
  +        {
  +            Map.Entry e = (Map.Entry) i.next();
  +            MethodSignature sig = (MethodSignature) e.getKey();
  +            BodyBuilder builder = (BodyBuilder) e.getValue();
  +
  +            // Each BodyBuilder is created and given a begin(), this is
  +            // the matching end()
  +
  +            builder.end();
  +
  +            classFab().addMethod(Modifier.PUBLIC, sig, builder.toString());
  +        }
  +    }
  +
       private Constructor findConstructor()
       {
           if (_classFab == null)
  @@ -372,5 +419,94 @@
           int dotx = baseName.lastIndexOf('.');
   
           return "$" + baseName.substring(dotx + 1) + "_" + _uid++;
  +    }
  +
  +    public BodyBuilder getBodyBuilderForMethod(Class interfaceClass, MethodSignature methodSignature)
  +    {
  +        addInterfaceIfNeeded(interfaceClass);
  +
  +        BodyBuilder result = (BodyBuilder) _incompleteMethods.get(methodSignature);
  +
  +        if (result == null)
  +        {
  +            result = createIncompleteMethod(methodSignature);
  +
  +            _incompleteMethods.put(methodSignature, result);
  +        }
  +
  +        return result;
  +    }
  +
  +    private void addInterfaceIfNeeded(Class interfaceClass)
  +    {
  +        if (interfaceClass.isAssignableFrom(_baseClass))
  +            return;
  +
  +        if (_addedInterfaces.contains(interfaceClass))
  +            return;
  +
  +        classFab().addInterface(interfaceClass);
  +        _addedInterfaces.add(interfaceClass);
  +    }
  +
  +    private BodyBuilder createIncompleteMethod(MethodSignature sig)
  +    {
  +        BodyBuilder result = new BodyBuilder();
  +
  +        // Matched inside finalizeIncompleteMethods()
  +
  +        result.begin();
  +
  +        try
  +        {
  +            Method m = _baseClass.getMethod(sig.getName(), sig.getParameterTypes());
  +
  +            if (!Modifier.isAbstract(m.getModifiers()))
  +                result.addln("super.{0}($$);", sig.getName());
  +        }
  +        catch (NoSuchMethodException ex)
  +        {
  +            // Good; no super-implementation to invoke.
  +        }
  +
  +        return result;
  +    }
  +
  +    public List findUnclaimedAbstractProperties()
  +    {
  +        List result = new ArrayList();
  +
  +        Iterator i = _properties.values().iterator();
  +
  +        while (i.hasNext())
  +        {
  +            PropertyDescriptor pd = (PropertyDescriptor) i.next();
  +
  +            String name = pd.getName();
  +
  +            if (_claimedProperties.contains(name))
  +                continue;
  +
  +            if (isAbstractProperty(pd))
  +                result.add(name);
  +        }
  +
  +        return result;
  +    }
  +
  +    /**
  +     * A property is abstract if either its read method or it write method is abstract. We could do
  +     * some additional checking to ensure that both are abstract if either is. Note that in many
  +     * cases, there will only be one accessor (a reader or a writer).
  +     */
  +    private boolean isAbstractProperty(PropertyDescriptor pd)
  +    {
  +        return isExistingAbstractMethod(pd.getReadMethod())
  +                || isExistingAbstractMethod(pd.getWriteMethod());
  +    }
  +
  +    private boolean isExistingAbstractMethod(Method m)
  +    {
  +        return m != null && Modifier.isAbstract(m.getModifiers());
       }
   }
  
  
  
  1.3       +47 -0     jakarta-tapestry/framework/src/java/org/apache/tapestry/enhance/EnhanceUtils.java
  
  Index: EnhanceUtils.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/enhance/EnhanceUtils.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- EnhanceUtils.java	30 Oct 2004 23:37:06 -0000	1.2
  +++ EnhanceUtils.java	3 Nov 2004 17:28:16 -0000	1.3
  @@ -14,6 +14,14 @@
   
   package org.apache.tapestry.enhance;
   
  +import java.lang.reflect.Modifier;
  +
  +import org.apache.hivemind.service.MethodSignature;
  +import org.apache.tapestry.IRequestCycle;
  +import org.apache.tapestry.engine.IPageLoader;
  +import org.apache.tapestry.event.PageEvent;
  +import org.apache.tapestry.spec.IComponentSpecification;
  +
   /**
    * Convienience methods needed by various parts of the enhancement subsystem.
    * 
  @@ -22,6 +30,14 @@
    */
   public class EnhanceUtils
   {
  +    static MethodSignature FINISH_LOAD_SIGNATURE = new MethodSignature(void.class, "finishLoad",
  +            new Class[]
  +            { IRequestCycle.class, IPageLoader.class, IComponentSpecification.class }, null);
  +
  +    static MethodSignature PAGE_DETACHED_SIGNATURE = new MethodSignature(void.class,
  +            "pageDetached", new Class[]
  +            { PageEvent.class }, null);
  +
       public static String createMutatorMethodName(String propertyName)
       {
           return "set" + upcase(propertyName);
  @@ -35,5 +51,36 @@
       private static String upcase(String name)
       {
           return name.substring(0, 1).toUpperCase() + name.substring(1);
  +    }
  +
  +    public static void createSimpleAccessor(EnhancementOperation op, String fieldName,
  +            String propertyName, Class propertyType)
  +    {
  +        String methodName = op.getAccessorMethodName(propertyName);
  +
  +        op.addMethod(
  +                Modifier.PUBLIC,
  +                new MethodSignature(propertyType, methodName, null, null),
  +                "return " + fieldName + ";");
  +    }
  +
  +    public static void createSimpleMutator(EnhancementOperation op, String fieldName,
  +            String propertyName, Class propertyType)
  +    {
  +        String methodName = createMutatorMethodName(propertyName);
  +
  +        op.addMethod(Modifier.PUBLIC, new MethodSignature(void.class, methodName, new Class[]
  +        { propertyType }, null), fieldName + " = $1;");
  +    }
  +
  +    public static void createSimpleProperty(EnhancementOperation op, String propertyName,
  +            Class propertyType)
  +    {
  +        String fieldName = "_$" + propertyName;
  +
  +        op.addField(fieldName, propertyType);
  +
  +        createSimpleAccessor(op, fieldName, propertyName, propertyType);
  +        createSimpleMutator(op, fieldName, propertyName, propertyType);
       }
   }
  
  
  
  1.1                  jakarta-tapestry/framework/src/java/org/apache/tapestry/enhance/AbstractPropertyWorker.java
  
  Index: AbstractPropertyWorker.java
  ===================================================================
  // Copyright 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.tapestry.enhance;
  
  import java.util.Iterator;
  
  import org.apache.hivemind.ErrorLog;
  import org.apache.hivemind.service.BodyBuilder;
  import org.apache.tapestry.IComponent;
  import org.apache.tapestry.event.PageDetachListener;
  
  /**
   * No, this class isn't abstract ... this worker locates abstract properties in the base component
   * class and provides a concrete implementation for them in the enhanced class.
   * 
   * @author Howard M. Lewis Ship
   * @since 3.1
   */
  public class AbstractPropertyWorker implements EnhancementWorker
  {
      private ErrorLog _errorLog;
  
      public void performEnhancement(EnhancementOperation op)
      {
          Iterator i = op.findUnclaimedAbstractProperties().iterator();
  
          while (i.hasNext())
          {
              String name = (String) i.next();
  
              try
              {
                  createProperty(op, name);
              }
              catch (Exception ex)
              {
                  _errorLog.error(
                          EnhanceMessages.errorAddingProperty(name, op.getBaseClass(), ex),
                          op.getSpecification().getLocation(),
                          ex);
              }
          }
      }
  
      private void createProperty(EnhancementOperation op, String name)
      {
          // This won't be null because its always for existing properties.
  
          Class propertyType = op.getPropertyType(name);
  
          String fieldName = "_$" + name;
          String defaultFieldName = fieldName + "$defaultValue";
  
          op.addField(fieldName, propertyType);
          op.addField(defaultFieldName, propertyType);
  
          EnhanceUtils.createSimpleAccessor(op, fieldName, name, propertyType);
          EnhanceUtils.createSimpleMutator(op, fieldName, name, propertyType);
  
          BodyBuilder finishLoadBody = op.getBodyBuilderForMethod(
                  IComponent.class,
                  EnhanceUtils.FINISH_LOAD_SIGNATURE);
  
          // Inside finish load, "snapshot" the value of the real field
  
          finishLoadBody.addln("{0} = {1};", defaultFieldName, fieldName);
  
          // When detaching the page, overwrite the field value with
          // the snapshot.
  
          BodyBuilder pageDetachedBody = op.getBodyBuilderForMethod(
                  PageDetachListener.class,
                  EnhanceUtils.PAGE_DETACHED_SIGNATURE);
  
          pageDetachedBody.addln("{0} = {1};", fieldName, defaultFieldName);
  
          // This is not all that necessary, but is proper.
  
          op.claimProperty(name);
      }
  
      public void setErrorLog(ErrorLog errorLog)
      {
          _errorLog = errorLog;
      }
  }
  
  
  1.5       +204 -0    jakarta-tapestry/framework/src/test/org/apache/tapestry/enhance/TestEnhancementOperation.java
  
  Index: TestEnhancementOperation.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/test/org/apache/tapestry/enhance/TestEnhancementOperation.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- TestEnhancementOperation.java	2 Nov 2004 13:30:00 -0000	1.4
  +++ TestEnhancementOperation.java	3 Nov 2004 17:28:16 -0000	1.5
  @@ -20,16 +20,20 @@
   import java.util.Set;
   
   import org.apache.hivemind.ApplicationRuntimeException;
  +import org.apache.hivemind.ClassResolver;
   import org.apache.hivemind.Location;
   import org.apache.hivemind.impl.DefaultClassResolver;
  +import org.apache.hivemind.service.BodyBuilder;
   import org.apache.hivemind.service.ClassFab;
   import org.apache.hivemind.service.ClassFactory;
   import org.apache.hivemind.service.MethodSignature;
   import org.apache.hivemind.service.impl.ClassFactoryImpl;
   import org.apache.hivemind.test.HiveMindTestCase;
   import org.apache.tapestry.BaseComponent;
  +import org.apache.tapestry.IComponent;
   import org.apache.tapestry.IPage;
   import org.apache.tapestry.components.Insert;
  +import org.apache.tapestry.event.PageDetachListener;
   import org.apache.tapestry.services.ComponentConstructor;
   import org.apache.tapestry.spec.IComponentSpecification;
   import org.easymock.MockControl;
  @@ -71,6 +75,23 @@
           }
       }
   
  +    public abstract static class UnclaimedAbstractPropertiesFixture
  +    {
  +        public abstract String getReadOnly();
  +
  +        public abstract void setWriteOnly(String value);
  +
  +        public void setConcrete(int i)
  +        {
  +            //
  +        }
  +
  +        public int getConcrete()
  +        {
  +            return -1;
  +        }
  +    }
  +
       public void testClaimedProperty()
       {
           EnhancementOperationImpl eo = new EnhancementOperationImpl();
  @@ -416,6 +437,189 @@
                   BaseComponent.class, cf);
   
           eo.forceEnhancement();
  +
  +        verifyControls();
  +    }
  +
  +    public void testFindUnclaimedAbstractProperties()
  +    {
  +        ClassResolver cr = (ClassResolver) newMock(ClassResolver.class);
  +        IComponentSpecification spec = (IComponentSpecification) newMock(IComponentSpecification.class);
  +        ClassFactory cf = (ClassFactory) newMock(ClassFactory.class);
  +
  +        replayControls();
  +
  +        EnhancementOperation eo = new EnhancementOperationImpl(cr, spec,
  +                UnclaimedAbstractPropertiesFixture.class, cf);
  +
  +        List l = eo.findUnclaimedAbstractProperties();
  +
  +        assertEquals(2, l.size());
  +        assertEquals(true, l.contains("readOnly"));
  +        assertEquals(true, l.contains("writeOnly"));
  +
  +        eo.claimProperty("readOnly");
  +
  +        l = eo.findUnclaimedAbstractProperties();
  +
  +        assertEquals(1, l.size());
  +        assertEquals(true, l.contains("writeOnly"));
  +
  +        eo.claimProperty("writeOnly");
  +
  +        l = eo.findUnclaimedAbstractProperties();
  +
  +        assertEquals(true, l.isEmpty());
  +
  +        verifyControls();
  +    }
  +
  +    public void testGetNewMethod()
  +    {
  +        ClassResolver cr = new DefaultClassResolver();
  +        MockControl specc = newControl(IComponentSpecification.class);
  +        IComponentSpecification spec = (IComponentSpecification) specc.getMock();
  +
  +        MockControl cfc = newControl(ClassFactory.class);
  +        ClassFactory cf = (ClassFactory) cfc.getMock();
  +        MockControl fabc = newControl(ClassFab.class);
  +        ClassFab fab = (ClassFab) fabc.getMock();
  +
  +        fab.addInterface(PageDetachListener.class);
  +
  +        cf.newClass("$BaseComponent_97", BaseComponent.class, cr.getClassLoader());
  +        cfc.setReturnValue(fab);
  +
  +        replayControls();
  +
  +        EnhancementOperationImpl eo = new EnhancementOperationImpl(cr, spec, BaseComponent.class,
  +                cf);
  +
  +        MethodSignature sig = EnhanceUtils.PAGE_DETACHED_SIGNATURE;
  +
  +        BodyBuilder b = eo.getBodyBuilderForMethod(PageDetachListener.class, sig);
  +
  +        assertEquals("{\n", b.toString());
  +
  +        verifyControls();
  +
  +        replayControls();
  +
  +        // Check that repeated calls return the same body builder and do not
  +        // keep adding methods.
  +
  +        assertSame(b, eo.getBodyBuilderForMethod(PageDetachListener.class, sig));
  +
  +        verifyControls();
  +
  +        fab.addMethod(Modifier.PUBLIC, sig, "{\n}\n");
  +        fabc.setReturnValue(null);
  +
  +        fab.createClass();
  +        fabc.setReturnValue(BaseComponent.class);
  +
  +        spec.getLocation();
  +        specc.setReturnValue(null);
  +
  +        replayControls();
  +
  +        eo.getConstructor();
  +
  +        assertEquals("{\n}\n", b.toString());
  +
  +        verifyControls();
  +    }
  +
  +    public void testGetExistingMethod()
  +    {
  +        ClassResolver cr = new DefaultClassResolver();
  +        MockControl specc = newControl(IComponentSpecification.class);
  +        IComponentSpecification spec = (IComponentSpecification) specc.getMock();
  +
  +        MockControl cfc = newControl(ClassFactory.class);
  +        ClassFactory cf = (ClassFactory) cfc.getMock();
  +        MockControl fabc = newControl(ClassFab.class);
  +        ClassFab fab = (ClassFab) fabc.getMock();
  +
  +        replayControls();
  +
  +        EnhancementOperationImpl eo = new EnhancementOperationImpl(cr, spec, BaseComponent.class,
  +                cf);
  +
  +        MethodSignature sig = EnhanceUtils.FINISH_LOAD_SIGNATURE;
  +
  +        BodyBuilder b = eo.getBodyBuilderForMethod(IComponent.class, sig);
  +
  +        assertEquals("{\n  super.finishLoad($$);\n", b.toString());
  +
  +        verifyControls();
  +
  +        cf.newClass("$BaseComponent_97", BaseComponent.class, cr.getClassLoader());
  +        cfc.setReturnValue(fab);
  +
  +        fab.addMethod(Modifier.PUBLIC, sig, "{\n  super.finishLoad($$);\n}\n");
  +        fabc.setReturnValue(null);
  +
  +        fab.createClass();
  +        fabc.setReturnValue(BaseComponent.class);
  +
  +        spec.getLocation();
  +        specc.setReturnValue(null);
  +
  +        replayControls();
  +
  +        eo.getConstructor();
  +
  +        verifyControls();
  +    }
  +
  +    public static abstract class ExistingAbstractMethodFixture extends BaseComponent implements
  +            PageDetachListener
  +    {
  +    }
  +
  +    public void getExistingAbstractMethod()
  +    {
  +        ClassResolver cr = new DefaultClassResolver();
  +        MockControl specc = newControl(IComponentSpecification.class);
  +        IComponentSpecification spec = (IComponentSpecification) specc.getMock();
  +
  +        MockControl cfc = newControl(ClassFactory.class);
  +        ClassFactory cf = (ClassFactory) cfc.getMock();
  +        MockControl fabc = newControl(ClassFab.class);
  +        ClassFab fab = (ClassFab) fabc.getMock();
  +
  +        replayControls();
  +
  +        EnhancementOperationImpl eo = new EnhancementOperationImpl(cr, spec,
  +                ExistingAbstractMethodFixture.class, cf);
  +
  +        MethodSignature sig = EnhanceUtils.PAGE_DETACHED_SIGNATURE;
  +
  +        BodyBuilder b = eo.getBodyBuilderForMethod(PageDetachListener.class, sig);
  +
  +        assertEquals("{\n", b.toString());
  +
  +        verifyControls();
  +
  +        cf.newClass("$ExitingAbstractMethodFixture_97", ExistingAbstractMethodFixture.class, cr
  +                .getClassLoader());
  +        cfc.setReturnValue(fab);
  +
  +        fab.addMethod(Modifier.PUBLIC, sig, "{\n}\n");
  +        fabc.setReturnValue(null);
  +
  +        fab.createClass();
  +        fabc.setReturnValue(BaseComponent.class);
  +
  +        spec.getLocation();
  +        specc.setReturnValue(null);
  +
  +        replayControls();
  +
  +        eo.getConstructor();
  +
  +        assertEquals("{\n}\n", b.toString());
   
           verifyControls();
       }
  
  
  
  1.1                  jakarta-tapestry/framework/src/test/org/apache/tapestry/enhance/TestAbstractPropertyWorker.java
  
  Index: TestAbstractPropertyWorker.java
  ===================================================================
  // Copyright 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.tapestry.enhance;
  
  import java.lang.reflect.Modifier;
  import java.util.Collections;
  
  import org.apache.hivemind.ApplicationRuntimeException;
  import org.apache.hivemind.ErrorLog;
  import org.apache.hivemind.Location;
  import org.apache.hivemind.service.BodyBuilder;
  import org.apache.hivemind.service.MethodSignature;
  import org.apache.hivemind.test.HiveMindTestCase;
  import org.apache.tapestry.BaseComponent;
  import org.apache.tapestry.IComponent;
  import org.apache.tapestry.event.PageDetachListener;
  import org.apache.tapestry.spec.IComponentSpecification;
  import org.easymock.MockControl;
  
  /**
   * Tests {@link org.apache.tapestry.enhance.AbstractPropertyWorker}.
   * 
   * @author Howard M. Lewis Ship
   * @since 3.1
   */
  public class TestAbstractPropertyWorker extends HiveMindTestCase
  {
      public void testSuccess()
      {
          MockControl opc = newControl(EnhancementOperation.class);
          EnhancementOperation op = (EnhancementOperation) opc.getMock();
  
          BodyBuilder finishLoadBody = new BodyBuilder();
          BodyBuilder pageDetachedBody = new BodyBuilder();
  
          op.findUnclaimedAbstractProperties();
          opc.setReturnValue(Collections.singletonList("fred"));
  
          op.getPropertyType("fred");
          opc.setReturnValue(String.class);
  
          op.addField("_$fred", String.class);
          op.addField("_$fred$defaultValue", String.class);
  
          op.getAccessorMethodName("fred");
          opc.setReturnValue("getFred");
  
          op.addMethod(
                  Modifier.PUBLIC,
                  new MethodSignature(String.class, "getFred", null, null),
                  "return _$fred;");
  
          op.addMethod(Modifier.PUBLIC, new MethodSignature(void.class, "setFred", new Class[]
          { String.class }, null), "_$fred = $1;");
  
          op.getBodyBuilderForMethod(IComponent.class, EnhanceUtils.FINISH_LOAD_SIGNATURE);
          opc.setReturnValue(finishLoadBody);
  
          op.getBodyBuilderForMethod(PageDetachListener.class, EnhanceUtils.PAGE_DETACHED_SIGNATURE);
          opc.setReturnValue(pageDetachedBody);
  
          op.claimProperty("fred");
  
          replayControls();
  
          new AbstractPropertyWorker().performEnhancement(op);
  
          assertEquals("_$fred$defaultValue = _$fred;\n", finishLoadBody.toString());
          assertEquals("_$fred = _$fred$defaultValue;\n", pageDetachedBody.toString());
  
          verifyControls();
      }
  
      public void testFailure()
      {
          MockControl opc = newControl(EnhancementOperation.class);
          EnhancementOperation op = (EnhancementOperation) opc.getMock();
  
          MockControl specc = newControl(IComponentSpecification.class);
          IComponentSpecification spec = (IComponentSpecification) specc.getMock();
  
          Location l = fabricateLocation(21);
  
          ErrorLog log = (ErrorLog) newMock(ErrorLog.class);
  
          op.findUnclaimedAbstractProperties();
          opc.setReturnValue(Collections.singletonList("fred"));
  
          Throwable ex = new ApplicationRuntimeException("Arbitrary error.");
  
          op.getPropertyType("fred");
          opc.setThrowable(ex);
  
          op.getBaseClass();
          opc.setReturnValue(BaseComponent.class);
  
          op.getSpecification();
          opc.setReturnValue(spec);
  
          spec.getLocation();
          specc.setReturnValue(l);
  
          log.error(EnhanceMessages.errorAddingProperty("fred", BaseComponent.class, ex), l, ex);
  
          replayControls();
  
          AbstractPropertyWorker w = new AbstractPropertyWorker();
          w.setErrorLog(log);
  
          w.performEnhancement(op);
  
          verifyControls();
      }
  }
  
  
  1.4       +18 -3     jakarta-tapestry/framework/src/descriptor/META-INF/tapestry.enhance.xml
  
  Index: tapestry.enhance.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/descriptor/META-INF/tapestry.enhance.xml,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- tapestry.enhance.xml	2 Nov 2004 03:32:42 -0000	1.3
  +++ tapestry.enhance.xml	3 Nov 2004 17:28:16 -0000	1.4
  @@ -75,9 +75,14 @@
       
       <!-- These can actually run in any order, so we don't specify before or after. -->
       
  -    <worker id="specified-property" object="service:SpecifiedPropertyWorker"/>
  -    <worker id="parameter" object="service:ParameterPropertyWorker"/>
  -    <worker id="inject" object="service:InjectWorker"/>
  +    <worker id="specified-property" object="service:SpecifiedPropertyWorker" before="abstract-property"/>
  +    <worker id="parameter" object="service:ParameterPropertyWorker" before="abstract-property"/>
  +    <worker id="inject" object="service:InjectWorker" before="abstract-property"/>
  +    
  +    <!-- This should be second to last, or at least, after all other workers have 
  +         added and claimed any properties they are going to. -->
  +    
  +    <worker id="abstract-property" object="service:AbstractPropertyWorker"/>
       
       <!-- This needs to be absolutely last -->
       
  @@ -114,6 +119,16 @@
         </construct>
       </invoke-factory>
       
  +  </service-point>
  +  
  +  <service-point id="AbstractPropertyWorker" interface="org.apache.tapestry.enhance.EnhancementWorker">
  +    
  +    Finds otherwise unclaimed abstract properties and converts them into concrete properties, as if
  +    they were defined in the specification.
  +    
  +    <invoke-factory>
  +      <construct class="org.apache.tapestry.enhance.AbstractPropertyWorker"/>
  +    </invoke-factory>
     </service-point>
     
   </module>
  
  
  
  1.5       +6 -5      jakarta-tapestry/src/documentation/content/xdocs/UsersGuide/state.xml
  
  Index: state.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/src/documentation/content/xdocs/UsersGuide/state.xml,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- state.xml	3 Nov 2004 13:53:43 -0000	1.4
  +++ state.xml	3 Nov 2004 17:28:16 -0000	1.5
  @@ -397,11 +397,12 @@
   </note>
   
   <note>
  -Properties defined this way may be either transient or persistent.  It is useful to define
  -even transient
  -properties using the &spec.property; element because doing so ensures that
  -the property will be properly reset at the end of the request (before the page
  -is returned to the pool for later reuse).
  +Properties defined this way may be either transient or persistent. For transient properties (properties
  +which do not persist between requests), it is only necessary to specify the property
  +if it has an initial value.  Tapestry scans the component class looking for abstract properties that don't
  +match up against component parameters or &spec.property; elements; for each of these unclaimed properties,
  +a concrete property is created. The property is a transient property, as if a &spec.property; element <em>did</em>
  +exist for it.
   </note>
   	
   <p>
  
  
  
  1.2       +20 -20    jakarta-tapestry/examples/Workbench/src/context/WEB-INF/Chart.page
  
  Index: Chart.page
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/examples/Workbench/src/context/WEB-INF/Chart.page,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Chart.page	28 Oct 2004 22:24:46 -0000	1.1
  +++ Chart.page	3 Nov 2004 17:28:16 -0000	1.2
  @@ -15,8 +15,8 @@
      limitations under the License.
   -->
   <!DOCTYPE page-specification PUBLIC
  -  "-//Apache Software Foundation//Tapestry Specification 3.0//EN" 
  -  "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">
  +  "-//Apache Software Foundation//Tapestry Specification 3.1//EN" 
  +  "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_1.dtd">
   
   <page-specification class="org.apache.tapestry.workbench.chart.ChartPage">
   
  @@ -32,45 +32,45 @@
     </bean>
   
     <component id="showError" type="ShowError">
  -     <binding name="delegate" expression="beans.delegate"/>
  +     <binding name="delegate" value="ognl:beans.delegate"/>
     </component>
   
     <component id="form" type="Form">
  -    <binding name="listener" expression="listeners.submit"/>
  -    <binding name="delegate" expression="beans.delegate"/>
  +    <binding name="listener" value="ognl:listeners.submit"/>
  +    <binding name="delegate" value="ognl:beans.delegate"/>
     </component>
   
     <component id="plotValues" type="Foreach">
  -    <binding name="source" expression="plotValues"/>
  -    <binding name="value" expression="plotValue"/>
  -    <static-binding name="element" value="tr"/>
  +    <binding name="source" value="ognl:plotValues"/>
  +    <binding name="value" value="ognl:plotValue"/>
  +    <binding name="element" value="tr"/>
     </component>
   
     <component id="inputName" type="ValidField">
  -  	<static-binding name="displayName" value="Name"/>
  -  	<binding name="value" expression="plotValue.name"/>
  -  	<binding name="validator" expression="beans.required"/>
  +  	<binding name="displayName" value="Name"/>
  +  	<binding name="value" value="ognl:plotValue.name"/>
  +  	<binding name="validator" value="ognl:beans.required"/>
     </component>
   
     <component id="inputValue" type="ValidField">	
  -  	<static-binding name="displayName" value="Value"/>
  -  	<binding name="value" expression="plotValue.value"/>
  -  	<binding name="validator" expression="beans.intValidator"/>
  -  	<static-binding name="type" value="int"/>
  +  	<binding name="displayName" value="Value"/>
  +  	<binding name="value" value="ognl:plotValue.value"/>
  +  	<binding name="validator" value="ognl:beans.intValidator"/>
  +  	<binding name="type" value="int"/>
     </component>
     
     <component id="inputMarked" type="Checkbox">
  -    <binding name="selected" expression="markedForDeletion"/>
  +    <binding name="selected" value="ognl:markedForDeletion"/>
     </component>
     
     <component id="add" type="Submit">
  -  	<binding name="listener" expression="listeners.add"/>
  -  	<static-binding name="label" value="Update and Add"/>
  +  	<binding name="listener" value="ognl:listeners.add"/>
  +  	<binding name="label" value="Update and Add"/>
     </component>
     
     <component id="delete" type="Submit">	
  -  	<binding name="listener" expression="listeners.delete"/>
  -  	<static-binding name="label" value="Delete Selected"/>
  +  	<binding name="listener" value="ognl:listeners.delete"/>
  +  	<binding name="label" value="Delete Selected"/>
     </component>
   
   </page-specification>
  
  
  
  1.2       +13 -16    jakarta-tapestry/examples/Workbench/src/context/WEB-INF/Border.jwc
  
  Index: Border.jwc
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/examples/Workbench/src/context/WEB-INF/Border.jwc,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Border.jwc	28 Oct 2004 22:24:46 -0000	1.1
  +++ Border.jwc	3 Nov 2004 17:28:16 -0000	1.2
  @@ -15,40 +15,37 @@
      limitations under the License.
   -->
   <!DOCTYPE component-specification PUBLIC
  -  "-//Apache Software Foundation//Tapestry Specification 3.0//EN"
  -  "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">
  +  "-//Apache Software Foundation//Tapestry Specification 3.1//EN"
  +  "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_1.dtd">
   
   <component-specification
     class="org.apache.tapestry.workbench.components.Border"
     allow-informal-parameters="no">
   
  -   <property-specification name="pageName" type="java.lang.String"/>
  -   <property-specification name="activePageName" type="java.lang.String"/>
  -
     <component id="tabCell" type="Any">
  -  	<static-binding name="element" value="td"/>
  -  	<binding name="background" expression="midTabAsset"/>
  +  	<binding name="element" value="td"/>
  +  	<binding name="background" value="ognl:midTabAsset"/>
     </component>
   
   
     <component id="pageLink" type="DirectLink">
  -    <binding name="listener" expression="listeners.selectPage"/>
  -    <binding name="parameters" expression="pageName"/>
  -    <binding name="stateful" expression="false"/>
  +    <binding name="listener" value="ognl:listeners.selectPage"/>
  +    <binding name="parameters" value="ognl:pageName"/>
  +    <binding name="stateful" value="ognl:false"/>
     </component>
   
     <component id="inputRequestDebug" type="Checkbox">
  -    <binding name="selected" expression="page.visit.requestDebug"/>
  -    <static-binding name="onclick">
  +    <binding name="selected" value="ognl:page.visit.requestDebug"/>
  +    <binding name="onclick">
       	javascript:this.form.submit();
  -    </static-binding>
  +    </binding>
     </component>
   
     <component id="inputDisableInspector" type="Checkbox">
  -    <binding name="selected" expression="page.visit.disableInspector"/>
  -    <static-binding name="onclick">
  +    <binding name="selected" value="ognl:page.visit.disableInspector"/>
  +    <binding name="onclick">
       	javascript:this.form.submit();
  -    </static-binding>
  +    </binding>
     </component>
   
     <context-asset name="activeLeft" path="images/tab-active-left.gif"/>
  
  
  
  1.4       +11 -14    jakarta-tapestry/framework/src/java/org/apache/tapestry/html/ExceptionDisplay.java
  
  Index: ExceptionDisplay.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/html/ExceptionDisplay.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- ExceptionDisplay.java	31 Oct 2004 22:57:46 -0000	1.3
  +++ ExceptionDisplay.java	3 Nov 2004 17:28:16 -0000	1.4
  @@ -15,7 +15,6 @@
   package org.apache.tapestry.html;
   
   import org.apache.tapestry.BaseComponent;
  -import org.apache.tapestry.IBinding;
   import org.apache.tapestry.IMarkupWriter;
   import org.apache.tapestry.IRequestCycle;
   import org.apache.tapestry.bean.EvenOdd;
  @@ -28,15 +27,19 @@
    * @author Howard Lewis Ship
    */
   
  -public class ExceptionDisplay extends BaseComponent
  +public abstract class ExceptionDisplay extends BaseComponent
   {
  +    public abstract ExceptionDescription[] getExceptions();
  +
       private ExceptionDescription _exception;
   
  -    private int _count;
  +    private EvenOdd _evenOdd;
  +
  +    public abstract int getIndex();
   
  -    private int _index;
  +    public abstract int getCount();
   
  -    private EvenOdd _evenOdd;
  +    public abstract void setCount(int count);
   
       /**
        * Each time the current exception is set, as a side effect, the evenOdd helper bean is reset to
  @@ -57,10 +60,9 @@
   
       protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
       {
  -        ExceptionDescription[] exceptions = (ExceptionDescription[]) getBinding("exceptions")
  -                .getObject("exceptions", ExceptionDescription[].class);
  +        ExceptionDescription[] exceptions = getExceptions();
   
  -        _count = exceptions.length;
  +        setCount(exceptions.length);
   
           try
           {
  @@ -75,13 +77,8 @@
           }
       }
   
  -    public void setIndex(int value)
  -    {
  -        _index = value;
  -    }
  -
       public boolean isLast()
       {
  -        return _index == (_count - 1);
  +        return getIndex() == (getCount() - 1);
       }
   }
  
  
  
  1.8       +2 -2      jakarta-tapestry/framework/src/java/org/apache/tapestry/AbstractComponent.java
  
  Index: AbstractComponent.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/AbstractComponent.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- AbstractComponent.java	2 Nov 2004 18:59:14 -0000	1.7
  +++ AbstractComponent.java	3 Nov 2004 17:28:16 -0000	1.8
  @@ -236,8 +236,8 @@
   
       /**
        * Registers this component as a listener of the page if it implements
  -     * {@link org.apache.tapestry.event.PageDetachListener}or
  -     * {@link org.apache.tapestry.event.PageRenderListener}.
  +     * {@link org.apache.tapestry.event.PageDetachListener},
  +     * {@link org.apache.tapestry.event.PageRenderListener}or {@link PageValidateListener}.
        * <p>
        * Invokes {@link #finishLoad()}. Subclasses may overide as needed, but must invoke this
        * implementation. {@link BaseComponent}loads its HTML template.
  
  
  
  1.17      +10 -32    jakarta-tapestry/eclipse/Tapestry-Workbench.launch
  
  Index: Tapestry-Workbench.launch
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/eclipse/Tapestry-Workbench.launch,v
  retrieving revision 1.16
  retrieving revision 1.17
  diff -u -r1.16 -r1.17
  --- Tapestry-Workbench.launch	12 Oct 2004 12:42:34 -0000	1.16
  +++ Tapestry-Workbench.launch	3 Nov 2004 17:28:16 -0000	1.17
  @@ -3,7 +3,6 @@
   <booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
   <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.mortbay.jetty.Server"/>
   <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="jetty.xml"/>
  -<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dorg.apache.tapestry.disable-caching=true"/>
   <listAttribute key="org.eclipse.jdt.launching.SOURCE_PATH">
   <listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry containerPath=&quot;JRE_LIB&quot; path=&quot;2&quot;     sourceAttachmentPath=&quot;JRE_SRC&quot; sourceRootPath=&quot;JRE_SRCROOT&quot; type=&quot;3&quot;/&gt; "/>
   <listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry internalArchive=&quot;/jakarta-tapestry/config&quot;     path=&quot;3&quot; type=&quot;2&quot;/&gt; "/>
  @@ -33,44 +32,23 @@
   <listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry containerPath=&quot;JDK_DIR/lib/tools.jar&quot; path=&quot;3&quot; type=&quot;3&quot;/&gt; "/>
   <listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry containerPath=&quot;JETTY_DIR/ext/ant.jar&quot; path=&quot;3&quot; type=&quot;3&quot;/&gt; "/>
   </listAttribute>
  +<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dorg.apache.tapestry.disable-caching=true"/>
   <listAttribute key="org.eclipse.debug.ui.favoriteGroups">
   <listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
   <listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
   </listAttribute>
  -<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
   <stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="jakarta-tapestry/examples/Workbench"/>
  -<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_SOURCE_PATH" value="false"/>
  +<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
   <stringAttribute key="org.eclipse.debug.ui.target_run_perspective" value="perspective_default"/>
  +<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_SOURCE_PATH" value="false"/>
  +<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="jakarta-tapestry"/>
   <listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry containerPath=&quot;JRE_LIB&quot; path=&quot;2&quot;     sourceAttachmentPath=&quot;JRE_SRC&quot; sourceRootPath=&quot;JRE_SRCROOT&quot; type=&quot;3&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry internalArchive=&quot;/jakarta-tapestry/config&quot;     path=&quot;3&quot; type=&quot;2&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry path=&quot;3&quot; projectName=&quot;jakarta-tapestry&quot; type=&quot;1&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry     internalArchive=&quot;/jakarta-tapestry/lib/ext/jakarta-oro-2.0.6.jar&quot;     path=&quot;3&quot; type=&quot;2&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry     internalArchive=&quot;/jakarta-tapestry/lib/ext/commons-logging-1.0.2.jar&quot;     path=&quot;3&quot; type=&quot;2&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry containerPath=&quot;JBOSS_DIR/client/jboss-j2ee.jar&quot;     path=&quot;3&quot; type=&quot;3&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry containerPath=&quot;JBOSS_DIR/server/all/lib/mail.jar&quot;     path=&quot;3&quot; type=&quot;3&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry     internalArchive=&quot;/jakarta-tapestry/lib/ext/commons-lang-1.0.jar&quot;     path=&quot;3&quot; type=&quot;2&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry     internalArchive=&quot;/jakarta-tapestry/lib/ext/ognl-2.6.3.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry     internalArchive=&quot;/jakarta-tapestry/lib/ext/bsf-2.3.0.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry containerPath=&quot;JETTY_DIR/lib/javax.servlet.jar&quot;     path=&quot;3&quot; type=&quot;3&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry containerPath=&quot;JETTY_DIR/ext/javax.xml.jaxp.jar&quot;     path=&quot;3&quot; type=&quot;3&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry containerPath=&quot;JETTY_DIR/ext/crimson.jar&quot;     path=&quot;3&quot; type=&quot;3&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry     internalArchive=&quot;/jakarta-tapestry/ext-dist/jdom-b8.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry     internalArchive=&quot;/jakarta-tapestry/ext-dist/junit.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry     internalArchive=&quot;/jakarta-tapestry/lib/ext/commons-beanutils-1.6.1.jar&quot;     path=&quot;3&quot; type=&quot;2&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry     internalArchive=&quot;/jakarta-tapestry/lib/ext/commons-digester-1.5.jar&quot;     path=&quot;3&quot; type=&quot;2&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry     internalArchive=&quot;/jakarta-tapestry/examples/Workbench/lib/jCharts-0.6.0.jar&quot;     path=&quot;3&quot; type=&quot;2&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry     internalArchive=&quot;/jakarta-tapestry/lib/ext/commons-collections-2.1.jar&quot;     path=&quot;3&quot; type=&quot;2&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry     internalArchive=&quot;/jakarta-tapestry/lib/runtime/log4j-1.2.6.jar&quot;     path=&quot;3&quot; type=&quot;2&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry     containerPath=&quot;JETTY_DIR/lib/org.mortbay.jetty-jdk1.2.jar&quot; path=&quot;3&quot; type=&quot;3&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry containerPath=&quot;JETTY_DIR/ext/jasper-compiler.jar&quot;     path=&quot;3&quot; type=&quot;3&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry containerPath=&quot;JETTY_DIR/ext/jasper-runtime.jar&quot;     path=&quot;3&quot; type=&quot;3&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry containerPath=&quot;JETTY_DIR/ext/ant.jar&quot; path=&quot;3&quot; type=&quot;3&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry containerPath=&quot;JDK_DIR/lib/tools.jar&quot; path=&quot;3&quot; type=&quot;3&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry     internalArchive=&quot;/jakarta-tapestry/lib/ext/commons-fileupload-1.0.jar&quot;     path=&quot;3&quot; type=&quot;2&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry     internalArchive=&quot;/jakarta-tapestry/lib/ext/javassist-2.5.1.jar&quot;     path=&quot;3&quot; type=&quot;2&quot;/&gt; "/>
  -<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;runtimeClasspathEntry     internalArchive=&quot;/jakarta-tapestry/lib/ext/commons-codec-1.2.jar&quot;     path=&quot;3&quot; type=&quot;2&quot;/&gt; "/>
  +<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry type=&quot;3&quot; path=&quot;1&quot; containerPath=&quot;JRE_LIB&quot;/&gt;&#10;"/>
  +<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#10;&lt;memento project=&quot;jakarta-tapestry&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
  +<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry type=&quot;3&quot; path=&quot;3&quot; containerPath=&quot;JETTY_DIR/lib/javax.servlet.jar&quot;/&gt;&#10;"/>
  +<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry type=&quot;3&quot; path=&quot;3&quot; containerPath=&quot;JETTY_DIR/lib/org.mortbay.jetty.jar&quot;/&gt;&#10;"/>
  +<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry type=&quot;3&quot; path=&quot;3&quot; containerPath=&quot;JETTY_DIR/lib/org.mortbay.jetty-jdk1.2.jar&quot;/&gt;&#10;"/>
  +<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry type=&quot;3&quot; path=&quot;3&quot; containerPath=&quot;JETTY_DIR/lib/org.mortbay.jmx.jar&quot;/&gt;&#10;"/>
   </listAttribute>
  -<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="jakarta-tapestry"/>
   <stringAttribute key="org.eclipse.debug.ui.target_debug_perspective" value="perspective_default"/>
   </launchConfiguration>
  
  
  

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