Author: hlship
Date: Tue Oct 24 13:16:48 2006
New Revision: 467463

URL: http://svn.apache.org/viewvc?view=rev&rev=467463
Log:
Switch tests to use nice mocks instead of strict mocks
Give greater control over invoking super class methods when adding new methods 
to a class
Add @Mixins and @MixinClasses annotation
Change ComponentWorker to collect mixin information (from @Mixins and 
@MixinClasses)
Update PageLoader to make use of declared instance mixins

Added:
    
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/MixinClasses.java
    
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Mixins.java
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/mixins.apt
Modified:
    
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableComponentModelImpl.java
    
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableEmbeddedComponentModelImpl.java
    
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentLifecycleMethodWorker.java
    
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentWorker.java
    
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java
    
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactory.java
    
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactoryImpl.java
    
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java
    
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/ComponentModel.java
    
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/EmbeddedComponentModel.java
    
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ClassTransformation.java
    
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
    
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TestBase.java
    
tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/model/ModelStrings.properties
    
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
    
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/InstanceMixin.java
    
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/model/MutableComponentModelImplTest.java
    
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentLifecycleMethodWorkerTest.java
    
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageLoaderImplTest.java
    
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java
    
tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/InstanceMixin.html

Added: 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/MixinClasses.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/MixinClasses.java?view=auto&rev=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/MixinClasses.java
 (added)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/MixinClasses.java
 Tue Oct 24 13:16:48 2006
@@ -0,0 +1,22 @@
+package org.apache.tapestry.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Used to attach one or more instance mixins to an embedded component. Each 
mixin is specified as a
+ * specific class. This annotation is only recognized when used in conjuction 
with the
+ * [EMAIL PROTECTED] Component} annotation.
+ * 
+ * @see Mixins
+ */
[EMAIL PROTECTED](FIELD)
[EMAIL PROTECTED]
[EMAIL PROTECTED](RUNTIME)
+public @interface MixinClasses {
+    Class[] value();
+}

Added: 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Mixins.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Mixins.java?view=auto&rev=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Mixins.java
 (added)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Mixins.java
 Tue Oct 24 13:16:48 2006
@@ -0,0 +1,24 @@
+package org.apache.tapestry.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Used to attach one ore more instance mixin to an embedded component. Each 
mixin is specified in
+ * terms of a logical mixin type name. This annotation is only recognized when 
used in conjuction
+ * with the [EMAIL PROTECTED] Component} annotation.
+ * 
+ * @see MixinClasses
+ */
[EMAIL PROTECTED](FIELD)
[EMAIL PROTECTED]
[EMAIL PROTECTED](RUNTIME)
+public @interface Mixins {
+
+    /** One or more mixin type names, from which actual mixin class names can 
be resolved. */
+    String[] value();
+}

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableComponentModelImpl.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableComponentModelImpl.java?view=diff&rev=467463&r1=467462&r2=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableComponentModelImpl.java
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableComponentModelImpl.java
 Tue Oct 24 13:16:48 2006
@@ -220,4 +220,8 @@
         return logicalFieldName;
     }
 
+    public boolean isRootClass()
+    {
+        return _parentModel == null;
+    }
 }

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableEmbeddedComponentModelImpl.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableEmbeddedComponentModelImpl.java?view=diff&rev=467463&r1=467462&r2=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableEmbeddedComponentModelImpl.java
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableEmbeddedComponentModelImpl.java
 Tue Oct 24 13:16:48 2006
@@ -14,14 +14,15 @@
 
 package org.apache.tapestry.internal.model;
 
-import static org.apache.tapestry.ioc.IOCUtilities.toSimpleId;
 import static org.apache.tapestry.util.CollectionFactory.newMap;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
 import org.apache.tapestry.internal.util.InternalUtils;
 import org.apache.tapestry.model.MutableEmbeddedComponentModel;
+import org.apache.tapestry.util.CollectionFactory;
 
 public class MutableEmbeddedComponentModelImpl implements 
MutableEmbeddedComponentModel
 {
@@ -35,8 +36,8 @@
 
     private Map<String, String> _parameters;
 
-    /** Maps for simple mixin name to fully qualified mixin class name. */
-    private Map<String, String> _mixins;
+    /** List of mixin class names. */
+    private List<String> _mixinClassNames;
 
     public MutableEmbeddedComponentModelImpl(String id, String componentType,
             String componentClassName, String declaredClass)
@@ -95,31 +96,27 @@
         return InternalUtils.get(_parameters, parameterName);
     }
 
-    public String getMixinClassName(String mixinName)
+    public List<String> getMixinClassNames()
     {
-        return InternalUtils.get(_mixins, mixinName);
-    }
+        if (_mixinClassNames == null)
+            return Collections.emptyList();
 
-    public List<String> getMixinNames()
-    {
-        return InternalUtils.sortedKeys(_mixins);
+        return Collections.unmodifiableList(_mixinClassNames);
     }
 
     public void addMixin(String mixinClassName)
     {
-        String simpleName = toSimpleId(mixinClassName);
-
-        if (_mixins == null)
+        if (_mixinClassNames == null)
         {
-            _mixins = newMap();
+            _mixinClassNames = CollectionFactory.newList();
         }
         else
         {
-            if (_mixins.containsKey(simpleName))
-                throw new 
IllegalArgumentException(ModelMessages.duplicateMixin(simpleName, _id));
+            if (_mixinClassNames.contains(mixinClassName))
+                throw new IllegalArgumentException(ModelMessages
+                        .duplicateMixin(mixinClassName, _id));
         }
 
-        _mixins.put(simpleName, mixinClassName);
+        _mixinClassNames.add(mixinClassName);
     }
-
 }

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentLifecycleMethodWorker.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentLifecycleMethodWorker.java?view=diff&rev=467463&r1=467462&r2=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentLifecycleMethodWorker.java
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentLifecycleMethodWorker.java
 Tue Oct 24 13:16:48 2006
@@ -60,23 +60,33 @@
     {
         List<MethodSignature> methods = 
transformation.findMethodsWithAnnotation(_methodAnnotation);
 
+        // Except in the root class, don't bother to add a new method unless 
there's something to
+        // call (beside super).
+
         if (methods.isEmpty())
             return;
 
         BodyBuilder builder = new BodyBuilder();
         builder.begin();
 
-        // Check to see if the event is aborted on entry (shouldn't happen), 
or from super-class
-        // implementation (a real possibility).
+        // If in a subclass, invoke the super class version first.
 
-        builder.addln(CHECK_ABORT_FLAG);
+        if (!model.isRootClass())
+        {
+            builder.addln("super.%s($$);", 
_lifecycleMethodSignature.getMethodName());
+            builder.addln(CHECK_ABORT_FLAG);
+        }
 
         for (MethodSignature sig : methods)
             addMethodCallToBody(builder, sig, transformation);
 
         builder.end();
 
-        transformation.extendMethod(_lifecycleMethodSignature, 
builder.toString());
+        // Let's see if this works; for base classes, we are adding an empty 
method the adding a
+        // non-empty
+        // method "on top of it".
+
+        transformation.addMethod(_lifecycleMethodSignature, 
builder.toString());
     }
 
     private void addMethodCallToBody(BodyBuilder builder, MethodSignature sig,

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentWorker.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentWorker.java?view=diff&rev=467463&r1=467462&r2=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentWorker.java
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentWorker.java
 Tue Oct 24 13:16:48 2006
@@ -15,21 +15,31 @@
 package org.apache.tapestry.internal.services;
 
 import org.apache.tapestry.annotations.Component;
+import org.apache.tapestry.annotations.MixinClasses;
+import org.apache.tapestry.annotations.Mixins;
 import org.apache.tapestry.internal.util.InternalUtils;
+import org.apache.tapestry.model.ComponentModel;
 import org.apache.tapestry.model.MutableComponentModel;
 import org.apache.tapestry.model.MutableEmbeddedComponentModel;
 import org.apache.tapestry.services.ClassTransformation;
+import org.apache.tapestry.services.ComponentClassResolver;
 import org.apache.tapestry.services.ComponentClassTransformWorker;
 import org.apache.tapestry.services.TransformConstants;
 
 /**
  * Finds fields with the [EMAIL PROTECTED] 
org.apache.tapestry.annotations.Component} annotation and updates
- * the model.
- * 
- * 
+ * the model. Also checks for the [EMAIL PROTECTED] Mixins} and [EMAIL 
PROTECTED] MixinClasses} annotations and uses them
+ * to update the [EMAIL PROTECTED] ComponentModel}.
  */
 public class ComponentWorker implements ComponentClassTransformWorker
 {
+    private final ComponentClassResolver _resolver;
+
+    public ComponentWorker(final ComponentClassResolver resolver)
+    {
+        _resolver = resolver;
+    }
+
     public void transform(ClassTransformation transformation, 
MutableComponentModel model)
     {
         for (String fieldName : 
transformation.findFieldsWithAnnotation(Component.class))
@@ -60,7 +70,37 @@
             transformation
                     
.extendMethod(TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE, body);
 
+            addMixinClasses(fieldName, transformation, embedded);
+            addMixinTypes(fieldName, transformation, embedded);
+
             transformation.claimField(fieldName, annotation);
+        }
+    }
+
+    private void addMixinClasses(String fieldName, ClassTransformation 
transformation,
+            MutableEmbeddedComponentModel model)
+    {
+        MixinClasses annotation = transformation.getFieldAnnotation(fieldName, 
MixinClasses.class);
+
+        if (annotation == null)
+            return;
+
+        for (Class c : annotation.value())
+            model.addMixin(c.getName());
+    }
+
+    private void addMixinTypes(String fieldName, ClassTransformation 
transformation,
+            MutableEmbeddedComponentModel model)
+    {
+        Mixins annotation = transformation.getFieldAnnotation(fieldName, 
Mixins.class);
+
+        if (annotation == null)
+            return;
+
+        for (String typeName : annotation.value())
+        {
+            String mixinClassName = 
_resolver.resolveMixinTypeToClassName(typeName);
+            model.addMixin(mixinClassName);
         }
     }
 

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java?view=diff&rev=467463&r1=467462&r2=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java
 Tue Oct 24 13:16:48 2006
@@ -150,12 +150,10 @@
                 "resources",
                 null);
 
-        MethodSignature sig = new MethodSignature(Modifier.PUBLIC, 
ComponentResources.class
-                .getName(), "getComponentResources", null, null);
+        MethodSignature sig = new MethodSignature(Modifier.PUBLIC | 
Modifier.FINAL,
+                ComponentResources.class.getName(), "getComponentResources", 
null, null);
 
-        // Override the default, empty implementation to simply return the 
field.
-
-        extendMethod(sig, "return " + _resourcesFieldName + ";");
+        addMethod(sig, "return " + _resourcesFieldName + ";");
     }
 
     @SuppressNullCheck
@@ -479,12 +477,17 @@
             newMethod.setBody(null);
 
             _ctClass.addMethod(newMethod);
+
+            MethodSignature sig = getMethodSignature(newMethod);
+
+            addMethodToDescription("add default", sig, "<default>");
         }
         catch (CannotCompileException ex)
         {
             throw new 
RuntimeException(ServicesMessages.errorAddingMethod(_ctClass, method
                     .getName(), ex), ex);
         }
+
     }
 
     /**
@@ -541,18 +544,43 @@
         CtClass[] parameters = buildCtClassList(signature.getParameterTypes());
         CtClass[] exceptions = buildCtClassList(signature.getExceptionTypes());
 
-        CtMethod method = new CtMethod(returnType, signature.getMethodName(), 
parameters, _ctClass);
+        String action = "add";
 
-        // TODO: Check for duplicate method add
+        try
+        {
+            CtMethod existing = 
_ctClass.getDeclaredMethod(signature.getMethodName(), parameters);
 
-        method.setModifiers(signature.getModifiers());
+            if (existing != null)
+            {
+                action = "replace";
+
+                _ctClass.removeMethod(existing);
+            }
+        }
+        catch (NotFoundException ex)
+        {
+            // That's ok. Kind of sloppy to rely on a thrown exception; wish 
getDeclaredMethod()
+            // would return null for
+            // that case. Alternately, we could maintain a set of the method 
signatures of declared
+            // or added methods.
+        }
 
         try
         {
+
+            CtMethod method = new CtMethod(returnType, 
signature.getMethodName(), parameters,
+                    _ctClass);
+
+            // TODO: Check for duplicate method add
+
+            method.setModifiers(signature.getModifiers());
+
             method.setBody(methodBody);
             method.setExceptionTypes(exceptions);
 
             _ctClass.addMethod(method);
+
+            _addedMethods.add(method);
         }
         catch (CannotCompileException ex)
         {
@@ -566,9 +594,7 @@
             throw new RuntimeException(ex);
         }
 
-        _addedMethods.add(method);
-
-        addMethodToDescription("add", signature, methodBody);
+        addMethodToDescription(action, signature, methodBody);
     }
 
     private CtClass[] buildCtClassList(String[] typeNames)

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactory.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactory.java?view=diff&rev=467463&r1=467462&r2=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactory.java
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactory.java
 Tue Oct 24 13:16:48 2006
@@ -89,4 +89,17 @@
      *            used to resolve the mixin class name
      */
     void addMixinByTypeName(ComponentPageElement component, String mixinType);
+
+    /**
+     * Adds a mixin to the element.
+     * <p>
+     * Sure, this isn't quite a <em>factory</em> method, but PEF has all the 
tools to accomplish
+     * this handy, as opposed to PageLoaderImpl.
+     * 
+     * @param component
+     *            the component to which a mixin will be added
+     * @param mixinClassName
+     *            fully qualified class name of the mixin
+     */
+    void addMixinByClassName(ComponentPageElement component, String 
mixinClassName);
 }

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactoryImpl.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactoryImpl.java?view=diff&rev=467463&r1=467462&r2=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactoryImpl.java
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageElementFactoryImpl.java
 Tue Oct 24 13:16:48 2006
@@ -183,6 +183,11 @@
     {
         String mixinClassName = 
_componentClassResolver.resolveMixinTypeToClassName(mixinType);
 
+        addMixinByClassName(component, mixinClassName);
+    }
+
+    public void addMixinByClassName(ComponentPageElement component, String 
mixinClassName)
+    {
         Instantiator mixinInstantiator = _componentInstantiatorSource
                 .findInstantiator(mixinClassName);
 

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java?view=diff&rev=467463&r1=467462&r2=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java
 Tue Oct 24 13:16:48 2006
@@ -318,7 +318,7 @@
                             embeddedComponentClassName,
                             startComponent.getLocation());
 
-                    addMixinsToComponent(newComponent, 
startComponent.getMixins());
+                    addMixinsToComponent(newComponent, embeddedModel, 
startComponent.getMixins());
 
                     add(loadingElement, activeComponent, newComponent);
 
@@ -366,13 +366,20 @@
         }
     }
 
-    private void addMixinsToComponent(ComponentPageElement component, String 
mixins)
+    private void addMixinsToComponent(ComponentPageElement component, 
EmbeddedComponentModel model,
+            String mixins)
     {
-        if (mixins == null)
-            return;
+        if (model != null)
+        {
+            for (String mixinClassName : model.getMixinClassNames())
+                _pageElementFactory.addMixinByClassName(component, 
mixinClassName);
+        }
 
-        for (String type : mixins.split(","))
-            _pageElementFactory.addMixinByTypeName(component, type);
+        if (mixins != null)
+        {
+            for (String type : mixins.split(","))
+                _pageElementFactory.addMixinByTypeName(component, type);
+        }
     }
 
     private void addParametersFromModel(EmbeddedComponentModel model,

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/ComponentModel.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/ComponentModel.java?view=diff&rev=467463&r1=467462&r2=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/ComponentModel.java
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/ComponentModel.java
 Tue Oct 24 13:16:48 2006
@@ -18,6 +18,7 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.tapestry.Resource;
+import org.apache.tapestry.annotations.ComponentClass;
 import org.apache.tapestry.annotations.Persist;
 
 /**
@@ -82,4 +83,13 @@
      * @throw IllegalArgumentException if the named field is not marked as 
persistent
      */
     String getFieldPersistenceStrategy(String fieldName);
+
+    /**
+     * Returns true if the modeled component is a root class, a component 
class whose parent does
+     * not have the [EMAIL PROTECTED] ComponentClass} annotation. This is 
often used to determine whether to
+     * invoke the super-class implementation of certain methods.
+     * 
+     * @return true if a root class, false if a subclass
+     */
+    boolean isRootClass();
 }

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/EmbeddedComponentModel.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/EmbeddedComponentModel.java?view=diff&rev=467463&r1=467462&r2=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/EmbeddedComponentModel.java
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/EmbeddedComponentModel.java
 Tue Oct 24 13:16:48 2006
@@ -43,14 +43,9 @@
     String getParameterValue(String parameterName);
 
     /**
-     * Returns the names of all mixins added to this component, sorted 
alphabetically. These are the
-     * "logical" names for each mixin, which is the class name of the mixin, 
less the package
-     * prefix.
-     * 
-     * @return the names of the added mixins, sorted alphabetically
+     * Returns the fully qualified class names of all mixins added to this 
component, sorted
+     * alphabetically.
      */
-    List<String> getMixinNames();
+    List<String> getMixinClassNames();
 
-    /** Returns the fully qualified class name of an added mixin, given the 
mixin's simple name. */
-    String getMixinClassName(String mixinName);
 }

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ClassTransformation.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ClassTransformation.java?view=diff&rev=467463&r1=467462&r2=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ClassTransformation.java
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ClassTransformation.java
 Tue Oct 24 13:16:48 2006
@@ -37,8 +37,6 @@
  * <p>
  * The majority of methods concern the <em>declared</em> members (field and 
methods) of a specific
  * class, rather than any fields or methods inherited from a base class.
- * 
- * 
  */
 public interface ClassTransformation
 {
@@ -224,7 +222,9 @@
      * change the return value, use the <code>$_</code> pseudo variable.
      * <p>
      * The method may be declared in the class, or may be inherited from a 
super-class. For
-     * inherited methods, a method is added that first invokes the super 
implementation.
+     * inherited methods, a method is added that first invokes the super 
implementation. Use
+     * [EMAIL PROTECTED] #addMethod(MethodSignature, String)} when it is 
necessary to control when the
+     * super-class method is invoked.
      * 
      * @param signature
      *            the signature of the method to extend
@@ -244,7 +244,13 @@
      */
     String getResourcesFieldName();
 
-    /** Adds a new method to the transformed class. */
+    /**
+     * Adds a new method to the transformed class. Replaces any existing 
method declared for the
+     * class. When overriding a super-class method, you should use
+     * [EMAIL PROTECTED] #extendMethod(MethodSignature, String)}, or you 
should remember to invoke the super
+     * class implemetation explicitly. Use this method to control when the 
super-class
+     * implementation is invoked.
+     */
     void addMethod(MethodSignature signature, String methodBody);
 
     /**

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java?view=diff&rev=467463&r1=467462&r2=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
 Tue Oct 24 13:16:48 2006
@@ -486,15 +486,14 @@
      * of the request</li>
      * <li>SetupRender, BeginRender, etc. -- correspond to component render 
phases and annotations</li>
      * </ul>
-     * 
-     * @param configuration
      */
     public static void contributeComponentClassTransformWorker(
             OrderedConfiguration<ComponentClassTransformWorker> configuration,
             ServiceLocator locator, 
@InjectService("tapestry.ioc.MasterObjectProvider")
             ObjectProvider objectProvider, @InjectService("InjectionProvider")
             InjectionProvider injectionProvider, @InjectService("Environment")
-            Environment environment)
+            Environment environment, 
@InjectService("tapestry.ComponentClassResolver")
+            ComponentClassResolver resolver)
     {
         // TODO: Proper scheduling of all of this. Since a given field or 
method should
         // only have a single annotation, the order doesn't matter so much, as 
long as
@@ -502,7 +501,7 @@
 
         configuration.add("Inject", new InjectWorker(objectProvider, locator, 
injectionProvider));
         configuration.add("Parameter", new ParameterWorker());
-        configuration.add("Component", new ComponentWorker());
+        configuration.add("Component", new ComponentWorker(resolver));
         configuration.add("Environment", new EnvironmentalWorker(environment));
 
         configuration.add("OnEvent", new OnEventWorker());

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TestBase.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TestBase.java?view=diff&rev=467463&r1=467462&r2=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TestBase.java
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TestBase.java
 Tue Oct 24 13:16:48 2006
@@ -28,7 +28,9 @@
  * Extends from [EMAIL PROTECTED] org.testng.Assert} to bring in all the 
public static assert methods without
  * requiring extra imports.
  * <p>
- * Provides common mock factory and mock trainer methods.
+ * Provides common mock factory and mock trainer methods. Uses a thread-local 
<em>nice</em> mock
+ * control for all created mocks. Nice mocks don't care about the order in 
which mock methods are
+ * invoked, and will return an appropriate null/0/false value for any 
unexpected method invocations.
  */
 public class TestBase extends Assert
 {
@@ -37,7 +39,7 @@
         @Override
         protected IMocksControl initialValue()
         {
-            return EasyMock.createStrictControl();
+            return EasyMock.createNiceControl();
         }
     }
 

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/model/ModelStrings.properties
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/model/ModelStrings.properties?view=diff&rev=467463&r1=467462&r2=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/model/ModelStrings.properties
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/model/ModelStrings.properties
 Tue Oct 24 13:16:48 2006
@@ -15,5 +15,5 @@
 duplicate-parameter=Parameter '%s' of component %s is already defined.
 duplicate-parameter-value=A value for parameter '%s' of embedded component %s 
(of component class %s) has already been provided.
 duplicate-component-id=Embedded component '%s' has already been defined for 
component class %s.
-duplicate-mixin=Mixin '%s' for component %s has already been defined.
+duplicate-mixin=Mixin %s (for component %s) has already been defined.
 missing-persistent-field=No field persistence strategy has been defined for 
field '%s'.

Added: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/mixins.apt
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/mixins.apt?view=auto&rev=467463
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/mixins.apt (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/mixins.apt Tue 
Oct 24 13:16:48 2006
@@ -0,0 +1,105 @@
+ ---
+ Component Mixins
+ ---
+ 
+Component Mixins
+
+  Tapestry 5 includes a radical feature, <component mixins>.  Component mixins 
are a tricky concept; it basically allows
+  a true component to be mixed together with special limited components called 
mixins.  The component plus its mixins are
+  represented as just a single tag in the component template, but all the 
behavior of all the elements.
+  
+  The planned uses for this are to add validation to user input fields, or to 
add Ajax effects and behaviors to all
+  sorts of components.
+  
+  You can think of a mixin as a kind of mashup for a component; it combines 
the behavior of the component
+  with the behavior of the mixin, and bundles it all in one place.
+  
+  Mixins are used in two different scenarios: <Instance mixins> and 
<Implementation mixins>.
+  
+Mixin Classes
+
+  Mixin classes are stored in a <<<mixins>>> sub-package, below the 
application (or library)
+  root package. This parallels where component and page classes are stored.
+  
+  Other than that, mixin classes are exactly the same as any other component 
class, including
+  the need for 
{{{../apidocs/org/apache/tapestry/annotations/ComponentClass.html}ComponentClass
 annotation}}.
+  
+Mixin Limitations
+
+  Currently, mixins are allowed to do anything a component can do, including 
parameters,
+  render phase methods.
+  
+  Mixins may not have a template. They integrate with the component strictly 
in terms of invoking
+  render phase methods.
+  
+  Mixins may have persistent fields, but currently, this is not implemented 
perfectly (there is a potential
+  for a name clash between a mixin and the component or another mixin).  Use 
persistent fields with
+  mixins with care ... or better yet, delegate persistence to the container 
using parameters.
+  
+Instance Mixins
+
+  An instance mixin is a mixin applied to a specific <instance> of a 
component.  This can be done
+  in the {{{templates.html}component template}} with the mixins attribute of 
the
+  \<comp\> element.  This is a comma-separated list of mixin names.
+  
+  Alternately, when the 
{{{../apidocs/org/apache/tapestry/annotations/Component.html}Component 
annotation}}
+  is used to define the component type, you may specify the mixins in two ways:
+  
+  * The {{{../apidocs/org/apache/tapestry/annotations/Mixins.html}Mixins 
annotation}} allows a list
+    of mixin names to be specified.
+    
+  * The 
{{{../apidocs/org/apache/tapestry/annotations/MixinClasses.html}MixinClasses 
annotation}}
+    allows a set of mixin class to be specified directly.
+    
+  []
+  
+  The former is often less verbose, and allows core mixins to be overridden 
with application-specific
+  mixins.  The later format is more specific and more friendly in terms of 
refactoring (renaming a 
+  mixin class will rename the entry in the MixinClasses annotation as well).
+  
+  Example:
+  
++----+
+
+  @Component(parameters=. . .) @Mixins({"Autocomplete", "DefaultFromCookie"})
+  private TextField _userId;
++----+
+
+  This example defines a component of type TextField and mixes in the 
<hypothetical> Autocomplete
+  and DefaultFromCookie mixins.  
+
+Mixin Parameters
+
+  Mixins are allowed to have parameters, just like components.
+  
+  When binding parameters (either in the template, or using the parameters 
attribute
+  of the Component annotation).
+  
+  Tapestry will match each parameter name against the parameters defined by 
each class
+  (which is to say, the component and each mixin).
+  
+  If the component and a mix both define a parameter with the same name, then 
the component wins:
+  the component's parameter will be bound, and the mixin's parameter will be 
unbound.
+  
+  Alternately, you may prefix the name of the parameter with the <unqualified> 
name of the Mixin class;
+  this eliminates the ambiguity.  Example:
+  
++-----+
+  @Component(parameters={"Autocomplete.id=auto", . . . }) 
@Mixins("Autocomplete", "DefaultFromCookie"})
+  private TextField _userId;
++-----+
+
+Implementation Mixins
+
+  Not yet implemented.
+  
+Render Phase Ordering
+
+  This is under consideration. To some degree, if a mixin and the core 
component both implement
+  methods for the same render phase, then the order of operations for 
BeginRender and AfterRender
+  should be reversed.
+  
+  Currently, the order is mixins, then render phase methods from the 
components' super classes, the render phase
+  methods in the component's class.
+  
+  
\ No newline at end of file

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java?view=diff&rev=467463&r1=467462&r2=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
 Tue Oct 24 13:16:48 2006
@@ -234,16 +234,27 @@
     {
         _selenium.open(BASE_URL);
 
+        final String[] dates =
+        { "Jun 13, 1999", "Jul 15, 2001", "Dec 4, 2005" };
+
         clickAndWait("link=InstanceMixin");
 
         String body = _selenium.getHtmlSource();
 
-        assertTrue(body.contains("[Jun 13, 1999]"));
+        for (String date : dates)
+        {
+            String snippet = String.format("[%s]", date);
+            assertTrue(body.contains(snippet), snippet);
+        }
 
         clickAndWait("link=Toggle emphasis");
 
         body = _selenium.getHtmlSource();
 
-        assertTrue(body.contains("[<em>Jun 13, 1999</em>]"));
+        for (String date : dates)
+        {
+            String snippet = String.format("[<em>%s</em>]", date);
+            assertTrue(body.contains(snippet), snippet);
+        }
     }
 }

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/InstanceMixin.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/InstanceMixin.java?view=diff&rev=467463&r1=467462&r2=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/InstanceMixin.java
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/InstanceMixin.java
 Tue Oct 24 13:16:48 2006
@@ -5,19 +5,40 @@
 import java.text.Format;
 import java.util.Calendar;
 
+import org.apache.tapestry.annotations.Component;
 import org.apache.tapestry.annotations.ComponentClass;
+import org.apache.tapestry.annotations.MixinClasses;
+import org.apache.tapestry.annotations.Mixins;
 import org.apache.tapestry.annotations.OnEvent;
 import org.apache.tapestry.annotations.Persist;
 import org.apache.tapestry.annotations.Retain;
+import org.apache.tapestry.integration.app1.components.Output;
+import org.apache.tapestry.integration.app1.mixins.Emphasis;
 
 @ComponentClass
 public class InstanceMixin
 {
+    @Component(parameters =
+    { "value=date2", "format=format", "test=showEmphasis" })
+    @Mixins("Emphasis")
+    private Output _output2;
+
+    @Component(parameters =
+    { "value=date3", "format=format", "test=showEmphasis" })
+    @MixinClasses(Emphasis.class)
+    private Output _output3;
+
     @Retain
     private final Format _format = 
DateFormat.getDateInstance(DateFormat.MEDIUM);
 
     @Retain
-    private final Date _date = new Date(99, Calendar.JUNE, 13);
+    private final Date _date1 = new Date(99, Calendar.JUNE, 13);
+
+    @Retain
+    private final Date _date2 = new Date(101, Calendar.JULY, 15);
+
+    @Retain
+    private final Date _date3 = new Date(105, Calendar.DECEMBER, 4);
 
     @Persist
     private boolean _showEmphasis;
@@ -27,9 +48,19 @@
         return _format;
     }
 
-    public Date getDate()
+    public Date getDate1()
+    {
+        return _date1;
+    }
+
+    public Date getDate2()
+    {
+        return _date2;
+    }
+
+    public Date getDate3()
     {
-        return _date;
+        return _date3;
     }
 
     public boolean getShowEmphasis()

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/model/MutableComponentModelImplTest.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/model/MutableComponentModelImplTest.java?view=diff&rev=467463&r1=467462&r2=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/model/MutableComponentModelImplTest.java
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/model/MutableComponentModelImplTest.java
 Tue Oct 24 13:16:48 2006
@@ -35,6 +35,25 @@
     private static final String CLASS_NAME = "org.example.components.Foo";
 
     @Test
+    public void root_class_vs_sub_class()
+    {
+        Resource r = newResource();
+        Log log = newLog();
+
+        replay();
+
+        MutableComponentModel model = new 
MutableComponentModelImpl(CLASS_NAME, log, r, null);
+
+        assertTrue(model.isRootClass());
+
+        MutableComponentModel subModel = new 
MutableComponentModelImpl(CLASS_NAME, log, r, model);
+
+        assertFalse(subModel.isRootClass());
+
+        verify();
+    }
+
+    @Test
     public void add_new_parameter()
     {
         Resource r = newResource();
@@ -255,13 +274,13 @@
                 "Fred",
                 COMPONENT_CLASS_NAME);
 
-        assertTrue(fred.getMixinNames().isEmpty());
+        assertTrue(fred.getMixinClassNames().isEmpty());
 
         verify();
     }
 
     @Test
-    public void mixin_names_added_as_simple_name()
+    public void mixin_class_names_remembered_in_order_added()
     {
         Resource r = newResource();
         Log log = newLog();
@@ -278,32 +297,7 @@
         fred.addMixin("zip.zop.Zoom");
         fred.addMixin("foo.bar.Baz");
 
-        assertEquals(fred.getMixinNames(), Arrays.asList("Baz", "Zoom"));
-
-        verify();
-    }
-
-    @Test
-    public void mixin_class_names_available_via_simple_name()
-    {
-        Resource r = newResource();
-        Log log = newLog();
-
-        replay();
-
-        MutableComponentModel model = new 
MutableComponentModelImpl(CLASS_NAME, log, r, null);
-
-        MutableEmbeddedComponentModel fred = model.addEmbeddedComponent(
-                "fred",
-                "Fred",
-                COMPONENT_CLASS_NAME);
-
-        fred.addMixin("zip.zop.Zoom");
-        fred.addMixin("foo.bar.Baz");
-
-        assertEquals(fred.getMixinClassName("Zoom"), "zip.zop.Zoom");
-        assertEquals(fred.getMixinClassName("Baz"), "foo.bar.Baz");
-        assertNull(fred.getMixinClassName("Gloop"));
+        assertEquals(fred.getMixinClassNames(), Arrays.asList("zip.zop.Zoom", 
"foo.bar.Baz"));
 
         verify();
     }
@@ -327,17 +321,19 @@
 
         try
         {
-            fred.addMixin("flim.flam.Zoom");
+            fred.addMixin("zip.zop.Zoom");
             unreachable();
         }
         catch (IllegalArgumentException ex)
         {
             assertEquals(
                     ex.getMessage(),
-                    "Mixin 'Zoom' for component fred has already been 
defined.");
+                    "Mixin zip.zop.Zoom (for component fred) has already been 
defined.");
         }
 
-        assertEquals(fred.getMixinNames(), Arrays.asList("Zoom"));
+        // Make sure it wasn't actually added.
+
+        assertEquals(fred.getMixinClassNames(), Arrays.asList("zip.zop.Zoom"));
 
         verify();
     }

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentLifecycleMethodWorkerTest.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentLifecycleMethodWorkerTest.java?view=diff&rev=467463&r1=467462&r2=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentLifecycleMethodWorkerTest.java
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentLifecycleMethodWorkerTest.java
 Tue Oct 24 13:16:48 2006
@@ -66,10 +66,10 @@
 
         train_findMethodsWithAnnotation(tf, SetupRender.class, sigs);
 
-        train_extendMethod(
+        train_addMethod(
                 tf,
                 TransformConstants.SETUP_RENDER_SIGNATURE,
-                "{if ($2.isAborted()) return;  aMethod(); }");
+                "{ super.setupRender($$); if ($2.isAborted()) return;  
aMethod(); }");
 
         replay();
 
@@ -82,6 +82,42 @@
     }
 
     @Test
+    public void method_in_base_class()
+    {
+        ClassTransformation tf = newClassTransformation();
+        MutableComponentModel model = newMutableComponentModel();
+
+        train_isRootClass(model, true);
+
+        List<MethodSignature> sigs = newList();
+
+        sigs.add(new MethodSignature("aMethod"));
+
+        train_findMethodsWithAnnotation(tf, SetupRender.class, sigs);
+
+        train_addMethod(
+                tf,
+                TransformConstants.SETUP_RENDER_SIGNATURE,
+                "{ aMethod(); }");
+
+        replay();
+
+        ComponentClassTransformWorker worker = new 
ComponentLifecycleMethodWorker(
+                TransformConstants.SETUP_RENDER_SIGNATURE, SetupRender.class);
+
+        worker.transform(tf, model);
+
+        verify();
+
+    }
+
+    protected final void train_isRootClass(MutableComponentModel model, 
boolean isRootClass)
+    {
+        model.isRootClass();
+        setReturnValue(isRootClass);
+    }
+
+    @Test
     public void method_with_markup_writer_parameter()
     {
         ClassTransformation tf = newClassTransformation();
@@ -94,10 +130,10 @@
 
         train_findMethodsWithAnnotation(tf, SetupRender.class, sigs);
 
-        train_extendMethod(
+        train_addMethod(
                 tf,
                 TransformConstants.SETUP_RENDER_SIGNATURE,
-                "{ if ($2.isAborted()) return;  aMethod($1); }");
+                "{ super.setupRender($$); if ($2.isAborted()) return; 
aMethod($1); }");
 
         replay();
 
@@ -124,10 +160,12 @@
 
         train_getClassName(tf, "biff.Baz");
 
-        train_extendMethod(
+        train_addMethod(
                 tf,
                 TransformConstants.SETUP_RENDER_SIGNATURE,
-                "{ if ($2.isAborted()) return; ",
+                "{",
+                "super.setupRender($$);",
+                "if ($2.isAborted()) return; ",
                 "if ($2.storeResult(($w) aMethod(), \"biff.Baz.aMethod()\")) 
return;",
                 "}");
 
@@ -156,10 +194,11 @@
         train_findMethodsWithAnnotation(tf, SetupRender.class, sigs);
         train_getClassName(tf, "foo.Bar");
 
-        train_extendMethod(
+        train_addMethod(
                 tf,
                 TransformConstants.SETUP_RENDER_SIGNATURE,
-                "{ if ($2.isAborted()) return;",
+                "{ super.setupRender($$);",
+                "if ($2.isAborted()) return;",
                 "if ($2.storeResult(($w) aMethod(), \"foo.Bar.aMethod()\")) 
return;",
                 "bMethod($1); }");
 

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageLoaderImplTest.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageLoaderImplTest.java?view=diff&rev=467463&r1=467462&r2=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageLoaderImplTest.java
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageLoaderImplTest.java
 Tue Oct 24 13:16:48 2006
@@ -14,7 +14,6 @@
 
 package org.apache.tapestry.internal.services;
 
-
 import java.util.Arrays;
 import java.util.Locale;
 
@@ -127,6 +126,8 @@
 
         train_getComponentType(emodel, "Barney");
 
+        train_getMixinClassNames(emodel);
+
         log.error(ServicesMessages.compTypeConflict("foo", "Fred", "Barney"));
 
         train_getComponentClassName(emodel, "foo.components.Barney");
@@ -161,5 +162,11 @@
         loader.loadPage(PAGE_CLASS_NAME, LOCALE);
 
         verify();
+    }
+
+    protected final void train_getMixinClassNames(EmbeddedComponentModel 
model, String... names)
+    {
+        model.getMixinClassNames();
+        setReturnValue(Arrays.asList(names));
     }
 }

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java?view=diff&rev=467463&r1=467462&r2=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java
 Tue Oct 24 13:16:48 2006
@@ -368,8 +368,6 @@
 
     private Instantiator newInstantiator(ComponentLifecycle component, 
ComponentModel model)
     {
-        getMocksControl().checkOrder(false);
-
         Instantiator ins = newMock(Instantiator.class);
 
         ins.getModel();

Modified: 
tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/InstanceMixin.html
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/InstanceMixin.html?view=diff&rev=467463&r1=467462&r2=467463
==============================================================================
--- 
tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/InstanceMixin.html
 (original)
+++ 
tapestry/tapestry5/tapestry-core/trunk/src/test/resources/org/apache/tapestry/integration/app1/pages/InstanceMixin.html
 Tue Oct 24 13:16:48 2006
@@ -1,10 +1,18 @@
 <t:comp type="Border" 
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd";>
 
-    <p> An important date: [<t:comp type="Output" 
+    <p> Date #1: [<t:comp type="Output" 
         mixins="Emphasis" 
-        value="prop:date" 
+        value="prop:date1" 
         test="prop:showEmphasis"
         format="prop:format"/>]
+    </p>
+    
+    <p>
+        Date #2: [<t:comp id="output2"/>]  (via @Mixins)
+    </p>
+    
+    <p>
+        Date #3: [<t:comp id="output3"/>] (via @MixinClasses)
     </p>
 
     <p>


Reply via email to