You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2007/01/24 23:26:05 UTC

svn commit: r499593 [1/2] - in /tapestry/tapestry5/tapestry-core/trunk/src: main/java/org/apache/tapestry/ main/java/org/apache/tapestry/annotations/ main/java/org/apache/tapestry/internal/ main/java/org/apache/tapestry/internal/model/ main/java/org/ap...

Author: hlship
Date: Wed Jan 24 14:26:02 2007
New Revision: 499593

URL: http://svn.apache.org/viewvc?view=rev&rev=499593
Log:
Move some of the Cookie support services into the internal package and module.
Add support for meta-data specified inside the ComponentClass annotation.
Change the default persistence stratgy inside the Perist annotation to be the empty string.
Refactor various methods related to field persistence.
A blank persistence strategy now causes a search up the component hierarchy for a meta data value for key tapestry.persistence-strategy.

Added:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalMessages.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/KeyValue.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/CookieSink.java
      - copied, changed from r497292, tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/CookieSink.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/CookieSource.java
      - copied, changed from r497292, tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/CookieSource.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/CookiesImpl.java
      - copied, changed from r497292, tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/CookiesImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/MetaWorker.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/InternalStrings.properties
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/CookiesImplTest.java
      - copied, changed from r497292, tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/services/CookiesImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/MetaWorkerTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/InheritedAnnotation.java
Removed:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/CookieSink.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/CookieSource.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/CookiesImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/services/CookiesImplTest.java
Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResources.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/ComponentClass.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Persist.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalComponentResources.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalComponentResourcesCommon.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/TapestryUtils.java
    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/services/ComponentWorker.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldManagerImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/InternalComponentResourcesImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StructureMessages.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.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/MutableComponentModel.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/Cookies.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldManager.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/TapestryTestCase.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/pagelevel/CookiesForPageTester.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/pagelevel/PageTester.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/structure/StructureStrings.properties
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/persist.apt
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/TapestryUtilsTest.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/InternalClassTransformationImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/NoOpPage.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PersistentFieldManagerImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/InternalComponentResourcesImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/pages/ParentClass.java

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResources.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResources.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResources.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResources.java Wed Jan 24 14:26:02 2007
@@ -42,7 +42,8 @@
 
     /**
      * Returns the {@link ComponentResources} for the container, or null if the this is the root
-     * component (that has no container).
+     * component (that has no container). As a special case, for a mixin, this returns the core
+     * component's resources.
      */
     ComponentResources getContainerResources();
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/ComponentClass.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/ComponentClass.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/ComponentClass.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/ComponentClass.java Wed Jan 24 14:26:02 2007
@@ -12,26 +12,30 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.annotations;
-
-import static java.lang.annotation.ElementType.TYPE;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * Describes a class as a component class, one for which a number of annotation-based
- * transformations should occur when loaded.
- * 
- * 
- */
-@Target(TYPE)
-@Retention(RUNTIME)
-@Documented
-@Inherited
-public @interface ComponentClass {
-
-}
+package org.apache.tapestry.annotations;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Describes a class as a component class, one for which a number of annotation-based
+ * transformations should occur when loaded. To be a component class, a class must be in an
+ * appropriate package, and must have the ComponentClass annotation (or extend from a base class
+ * that does have the annotation).
+ */
+@Target(TYPE)
+@Retention(RUNTIME)
+@Documented
+public @interface ComponentClass {
+    /**
+     * Allows meta data associated with the class to be specified. Meta data is in the form of
+     * key/data pairs.
+     * 
+     * @return
+     */
+    String[] meta() default {};
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Persist.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Persist.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Persist.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Persist.java Wed Jan 24 14:26:02 2007
@@ -12,30 +12,40 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-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;
-
-import org.apache.tapestry.services.Session;
-
-/**
- * Identifies a field as persistent, meaning its value persists from one request to the next.
- * Different strategies exist for how this is accomplished, the most common being the default,
- * "session", which stores the field's value in the {@link Session}.
- */
-@Target(FIELD)
-@Documented
-@Retention(RUNTIME)
-public @interface Persist {
-
-    /**
-     * The strategy used to persist the value; the default is "session", meaning indefintely inside
-     * the {@link Session}.
-     */
-    String value() default "session";
-}
+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;
+
+import org.apache.tapestry.services.Session;
+
+/**
+ * Identifies a field as persistent, meaning its value persists from one request to the next.
+ * Different strategies exist for how this is accomplished, the most common being the default,
+ * "session", which stores the field's value in the {@link Session}.
+ * <p>
+ * In most cases, the value will be omitted and will default to the empty string. This forces a
+ * search for the correct strategy. Starting with the component (or mixin) itself, a check is made
+ * for the {@link ComponentClass#meta() meta data property}
+ * <code>tapestry.persistence-strategy</code>. If a value is found, it is used, otherwise the
+ * search continues up the inheritance hierarchy, towards the page. If not found, then the "session"
+ * strategy is used.
+ * <p>
+ * In this way, the session persistence strategy for a component and all of its sub-components can
+ * be controlled by the containing component.
+ */
+@Target(FIELD)
+@Documented
+@Retention(RUNTIME)
+public @interface Persist {
+
+    /**
+     * The strategy used to persist the value. The default value, the empty string, allows
+     * persistence to be decided by the containing component and component hierarchy.
+     */
+    String value() default "";
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalComponentResources.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalComponentResources.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalComponentResources.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalComponentResources.java Wed Jan 24 14:26:02 2007
@@ -15,7 +15,9 @@
 package org.apache.tapestry.internal;
 
 import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.internal.structure.Page;
 import org.apache.tapestry.runtime.Component;
+import org.apache.tapestry.services.PersistentFieldManager;
 
 /**
  * An extension of {@link org.apache.tapestry.ComponentResources} that represents additional methods
@@ -26,8 +28,7 @@
         InternalComponentResourcesCommon
 {
     /**
-     * Reads the value of a parameter, via the parameter's
-     * {@link org.apache.tapestry.Binding}.
+     * Reads the value of a parameter, via the parameter's {@link org.apache.tapestry.Binding}.
      * 
      * @param <T>
      * @param parameterName
@@ -51,9 +52,9 @@
     <T> void writeParameter(String parameterName, T parameterValue);
 
     /**
-     * Returns true if the named parameter's {@link org.apache.tapestry.Binding} is
-     * invariant, false if otherwise, or if the parameter is not bound. Invariant bindings are
-     * cached more aggresively than variant bindings.
+     * Returns true if the named parameter's {@link org.apache.tapestry.Binding} is invariant, false
+     * if otherwise, or if the parameter is not bound. Invariant bindings are cached more
+     * aggresively than variant bindings.
      * 
      * @param parameterName
      *            the name of parameter to check for invariance
@@ -66,4 +67,12 @@
      * component to which the mixin is attached.
      */
     Component getCoreComponent();
+
+    /**
+     * Posts a change to a persistent field. If the component is still loading, then this change is
+     * ignored. Otherwise, it is propogated, via the
+     * {@link Page#persistFieldChange(org.apache.tapestry.internal.structure.ComponentPageElement, String, Object) page}
+     * to the {@link PersistentFieldManager}.
+     */
+    void persistFieldChange(String fieldName, Object newValue);
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalComponentResourcesCommon.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalComponentResourcesCommon.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalComponentResourcesCommon.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalComponentResourcesCommon.java Wed Jan 24 14:26:02 2007
@@ -16,10 +16,8 @@
 
 import org.apache.tapestry.Binding;
 import org.apache.tapestry.internal.structure.ComponentPageElement;
-import org.apache.tapestry.internal.structure.Page;
 import org.apache.tapestry.runtime.Component;
 import org.apache.tapestry.runtime.RenderQueue;
-import org.apache.tapestry.services.PersistentFieldManager;
 
 /**
  * Operations shared by {@link InternalComponentResources} and {@link ComponentPageElement}.
@@ -36,14 +34,6 @@
      * @return the value stored for the field, or null if no value is currently stored
      */
     Object getFieldChange(String fieldName);
-
-    /**
-     * Posts a change to a persistent field. If the component is still loading, then this change is
-     * ignored. Otherwise, it is propogated, via the
-     * {@link Page#persistFieldChange(org.apache.tapestry.internal.structure.ComponentPageElement, String, Object) page}
-     * to the {@link PersistentFieldManager}.
-     */
-    void persistFieldChange(String fieldName, Object newValue);
 
     /** Checks to see if there is a value stored for the indicated field. */
     boolean hasFieldChange(String fieldName);

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalMessages.java?view=auto&rev=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalMessages.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalMessages.java Wed Jan 24 14:26:02 2007
@@ -0,0 +1,28 @@
+// Copyright 2007 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.internal;
+
+import org.apache.tapestry.ioc.Messages;
+import org.apache.tapestry.ioc.internal.util.MessagesImpl;
+
+final class InternalMessages
+{
+    private static final Messages MESSAGES = MessagesImpl.forClass(InternalMessages.class);
+
+    static String badKeyValue(String input)
+    {
+        return MESSAGES.format("bad-key-value", input);
+    }
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/KeyValue.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/KeyValue.java?view=auto&rev=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/KeyValue.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/KeyValue.java Wed Jan 24 14:26:02 2007
@@ -0,0 +1,40 @@
+// Copyright 2007 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.internal;
+
+/** A key/value pair. */
+public class KeyValue
+{
+    private final String _key;
+
+    private final String _value;
+
+    public KeyValue(final String key, final String value)
+    {
+        _key = key;
+        _value = value;
+    }
+
+    public String getKey()
+    {
+        return _key;
+    }
+
+    public String getValue()
+    {
+        return _value;
+    }
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/TapestryUtils.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/TapestryUtils.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/TapestryUtils.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/TapestryUtils.java Wed Jan 24 14:26:02 2007
@@ -163,4 +163,24 @@
 
         return new SelectModelImpl(null, options);
     }
+
+    /**
+     * Parses a key/value pair where the key and the value are seperated by an equals sign. The key
+     * and value are trimmed of leading and trailing whitespace, and returned as a {@link KeyValue}.
+     * 
+     * @param input
+     * @return
+     */
+    public static KeyValue parseKeyValue(String input)
+    {
+        int pos = input.indexOf('=');
+
+        if (pos < 1)
+            throw new IllegalArgumentException(InternalMessages.badKeyValue(input));
+
+        String key = input.substring(0, pos);
+        String value = input.substring(pos + 1);
+
+        return new KeyValue(key.trim(), value.trim());
+    }
 }

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=499593&r1=499592&r2=499593
==============================================================================
--- 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 Wed Jan 24 14:26:02 2007
@@ -60,6 +60,8 @@
 
     private boolean _mixinAfter;
 
+    private Map<String, String> _metaData;
+
     public MutableComponentModelImpl(String componentClassName, Log log, Resource baseResource,
             ComponentModel parentModel)
     {
@@ -278,6 +280,29 @@
     public void setMixinAfter(boolean mixinAfter)
     {
         _mixinAfter = mixinAfter;
+    }
+
+    public void setMeta(String key, String value)
+    {
+        notBlank(key, "key");
+        notBlank(value, "value");
+
+        if (_metaData == null)
+            _metaData = newMap();
+
+        // TODO: Error if duplicate?
+
+        _metaData.put(key, value);
+    }
+
+    public String getMeta(String key)
+    {
+        String result = InternalUtils.get(_metaData, key);
+
+        if (result == null && _parentModel != null)
+            result = _parentModel.getMeta(key);
+
+        return result;
     }
 
 }

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=499593&r1=499592&r2=499593
==============================================================================
--- 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 Wed Jan 24 14:26:02 2007
@@ -12,111 +12,108 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.internal.services;
-
-import org.apache.tapestry.annotations.Component;
-import org.apache.tapestry.annotations.MixinClasses;
-import org.apache.tapestry.annotations.Mixins;
+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.KeyValue;
+import org.apache.tapestry.internal.TapestryUtils;
 import org.apache.tapestry.ioc.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 {@link org.apache.tapestry.annotations.Component} annotation and updates
- * the model. Also checks for the {@link Mixins} and {@link MixinClasses} annotations and uses them
- * to update the {@link 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))
-        {
-            Component annotation = transformation.getFieldAnnotation(fieldName, Component.class);
-
-            String id = annotation.id();
-
-            if (InternalUtils.isBlank(id))
-                id = InternalUtils.stripMemberPrefix(fieldName);
-
-            String type = transformation.getFieldType(fieldName);
-
-            MutableEmbeddedComponentModel embedded = model.addEmbeddedComponent(id, annotation
-                    .type(), type);
-
-            addParameters(embedded, annotation.parameters());
-
-            transformation.makeReadOnly(fieldName);
-
-            String body = String.format(
-                    "%s = (%s) %s.getEmbeddedComponent(\"%s\");",
-                    fieldName,
-                    type,
-                    transformation.getResourcesFieldName(),
-                    id);
-
-            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);
-        }
-    }
-
-    private void addParameters(MutableEmbeddedComponentModel embedded, String[] parameters)
-    {
-        for (String parameter : parameters)
-        {
-            int colonx = parameter.indexOf("=");
-
-            // TODO: Malformed strings
-
-            String name = parameter.substring(0, colonx).trim();
-            String value = parameter.substring(colonx + 1).trim();
-
-            embedded.addParameter(name, value);
-        }
-    }
-
-}
+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 {@link org.apache.tapestry.annotations.Component} annotation and updates
+ * the model. Also checks for the {@link Mixins} and {@link MixinClasses} annotations and uses them
+ * to update the {@link 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))
+        {
+            Component annotation = transformation.getFieldAnnotation(fieldName, Component.class);
+
+            String id = annotation.id();
+
+            if (InternalUtils.isBlank(id))
+                id = InternalUtils.stripMemberPrefix(fieldName);
+
+            String type = transformation.getFieldType(fieldName);
+
+            MutableEmbeddedComponentModel embedded = model.addEmbeddedComponent(id, annotation
+                    .type(), type);
+
+            addParameters(embedded, annotation.parameters());
+
+            transformation.makeReadOnly(fieldName);
+
+            String body = String.format(
+                    "%s = (%s) %s.getEmbeddedComponent(\"%s\");",
+                    fieldName,
+                    type,
+                    transformation.getResourcesFieldName(),
+                    id);
+
+            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);
+        }
+    }
+
+    private void addParameters(MutableEmbeddedComponentModel embedded, String[] parameters)
+    {
+        for (String parameter : parameters)
+        {
+            KeyValue kv = TapestryUtils.parseKeyValue(parameter);
+
+            embedded.addParameter(kv.getKey(), kv.getValue());
+        }
+    }
+
+}

Copied: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/CookieSink.java (from r497292, tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/CookieSink.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/CookieSink.java?view=diff&rev=499593&p1=tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/CookieSink.java&r1=497292&p2=tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/CookieSink.java&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/CookieSink.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/CookieSink.java Wed Jan 24 14:26:02 2007
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.services;
+package org.apache.tapestry.internal.services;
 
 import javax.servlet.http.Cookie;
 

Copied: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/CookieSource.java (from r497292, tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/CookieSource.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/CookieSource.java?view=diff&rev=499593&p1=tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/CookieSource.java&r1=497292&p2=tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/CookieSource.java&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/CookieSource.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/CookieSource.java Wed Jan 24 14:26:02 2007
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.services;
+package org.apache.tapestry.internal.services;
 
 import javax.servlet.http.Cookie;
 

Copied: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/CookiesImpl.java (from r497292, tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/CookiesImpl.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/CookiesImpl.java?view=diff&rev=499593&p1=tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/CookiesImpl.java&r1=497292&p2=tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/CookiesImpl.java&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/CookiesImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/CookiesImpl.java Wed Jan 24 14:26:02 2007
@@ -12,17 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.services;
+package org.apache.tapestry.internal.services;
 
 import javax.servlet.http.Cookie;
 
-import org.apache.tapestry.internal.services.ContextPathSource;
+import org.apache.tapestry.services.Cookies;
 
 /**
  * Implementation of the {@link org.apache.tapestry.services.Cookies} service interface.
- * 
- * @author Howard Lewis Ship
- * @since 4.0
  */
 public class CookiesImpl implements Cookies
 {

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java Wed Jan 24 14:26:02 2007
@@ -21,6 +21,8 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import javax.servlet.http.Cookie;
+
 import org.apache.commons.logging.Log;
 import org.apache.tapestry.Binding;
 import org.apache.tapestry.ComponentResources;
@@ -90,6 +92,8 @@
 
     private final ThreadLocale _threadLocale;
 
+    private final RequestGlobals _requestGlobals;
+
     public InternalModule(@InjectService("ComponentInstantiatorSource")
     ComponentInstantiatorSource componentInstantiatorSource, @InjectService("UpdateListenerHub")
     UpdateListenerHub updateListenerHub, @InjectService("tapestry.ioc.ThreadCleanupHub")
@@ -101,7 +105,8 @@
             ChainBuilder chainBuilder, @Inject("infrastructure:Request")
             Request request, @Inject("infrastructure:Response")
             Response response, @InjectService("tapestry.ioc.ThreadLocale")
-            ThreadLocale threadLocale)
+            ThreadLocale threadLocale, @Inject("infrastructure:RequestGlobals")
+            RequestGlobals requestGlobals)
     {
         _componentInstantiatorSource = componentInstantiatorSource;
         _updateListenerHub = updateListenerHub;
@@ -112,6 +117,7 @@
         _request = request;
         _response = response;
         _threadLocale = threadLocale;
+        _requestGlobals = requestGlobals;
     }
 
     public ComponentClassTransformer buildComponentClassTransformer(
@@ -594,4 +600,29 @@
                 componentClassResolver);
     }
 
- }
\ No newline at end of file
+    public CookieSource buildCookieSource()
+    {
+        return new CookieSource()
+        {
+
+            public Cookie[] getCookies()
+            {
+                return _requestGlobals.getHTTPServletRequest().getCookies();
+            }
+
+        };
+    }
+
+    public CookieSink buildCookieSink()
+    {
+        return new CookieSink()
+        {
+
+            public void addCookie(Cookie cookie)
+            {
+                _requestGlobals.getHTTPServletResponse().addCookie(cookie);
+            }
+
+        };
+    }
+}
\ No newline at end of file

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/MetaWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/MetaWorker.java?view=auto&rev=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/MetaWorker.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/MetaWorker.java Wed Jan 24 14:26:02 2007
@@ -0,0 +1,45 @@
+// Copyright 2007 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.internal.services;
+
+import org.apache.tapestry.annotations.ComponentClass;
+import org.apache.tapestry.internal.KeyValue;
+import org.apache.tapestry.internal.TapestryUtils;
+import org.apache.tapestry.model.MutableComponentModel;
+import org.apache.tapestry.services.ClassTransformation;
+import org.apache.tapestry.services.ComponentClassTransformWorker;
+
+/**
+ * Checks for any meta-data in the {@link ComponentClass} annotation, and adds it to the model.
+ */
+public class MetaWorker implements ComponentClassTransformWorker
+{
+
+    public void transform(ClassTransformation transformation, MutableComponentModel model)
+    {
+        ComponentClass annotation = transformation.getAnnotation(ComponentClass.class);
+
+        if (annotation == null)
+            return;
+
+        for (String meta : annotation.meta())
+        {
+            KeyValue kv = TapestryUtils.parseKeyValue(meta);
+
+            model.setMeta(kv.getKey(), kv.getValue());
+        }
+    }
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldManagerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldManagerImpl.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldManagerImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldManagerImpl.java Wed Jan 24 14:26:02 2007
@@ -12,59 +12,99 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.internal.services;
-
-import java.util.Collection;
-import java.util.Map;
-
+package org.apache.tapestry.internal.services;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.tapestry.ComponentResources;
 import org.apache.tapestry.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry.ioc.internal.util.InternalUtils;
-import org.apache.tapestry.services.PersistentFieldBundle;
-import org.apache.tapestry.services.PersistentFieldChange;
-import org.apache.tapestry.services.PersistentFieldManager;
-import org.apache.tapestry.services.PersistentFieldStrategy;
-
-public class PersistentFieldManagerImpl implements PersistentFieldManager
-{
-    private final Map<String, PersistentFieldStrategy> _strategies;
-
-    public PersistentFieldManagerImpl(final Map<String, PersistentFieldStrategy> strategies)
-    {
-        _strategies = strategies;
-    }
-
-    private PersistentFieldStrategy getStrategy(String strategyName)
-    {
-        PersistentFieldStrategy result = _strategies.get(strategyName);
-
-        if (result == null)
-        {
-            String catalog = InternalUtils.joinSorted(_strategies.keySet());
-
-            throw new RuntimeException(ServicesMessages.unknownPersistentFieldStrategy(
-                    strategyName,
-                    catalog));
-        }
-
-        return result;
-    }
-
-    public PersistentFieldBundle gatherChanges(String pageName)
-    {
-        Collection<PersistentFieldChange> allChanges = CollectionFactory.newList();
-
-        for (PersistentFieldStrategy strategy : _strategies.values())
-        {
-            allChanges.addAll(strategy.gatherFieldChanges(pageName));
-        }
-
-        return new PersistentFieldBundleImpl(allChanges);
-    }
-
-    public void postChange(String pageName, String componentId, String fieldName, String strategy,
-            Object newValue)
-    {
-        getStrategy(strategy).postChange(pageName, componentId, fieldName, newValue);
-    }
-
-}
+import org.apache.tapestry.model.ComponentModel;
+import org.apache.tapestry.services.PersistentFieldBundle;
+import org.apache.tapestry.services.PersistentFieldChange;
+import org.apache.tapestry.services.PersistentFieldManager;
+import org.apache.tapestry.services.PersistentFieldStrategy;
+
+public class PersistentFieldManagerImpl implements PersistentFieldManager
+{
+    static final String META_KEY = "tapestry.persistence-strategy";
+
+    static final String DEFAULT_STRATEGY = "session";
+
+    private final Map<String, PersistentFieldStrategy> _strategies;
+
+    public PersistentFieldManagerImpl(final Map<String, PersistentFieldStrategy> strategies)
+    {
+        _strategies = strategies;
+    }
+
+    private PersistentFieldStrategy getStrategy(String strategyName)
+    {
+        PersistentFieldStrategy result = _strategies.get(strategyName);
+
+        if (result == null)
+        {
+            String catalog = InternalUtils.joinSorted(_strategies.keySet());
+
+            throw new RuntimeException(ServicesMessages.unknownPersistentFieldStrategy(
+                    strategyName,
+                    catalog));
+        }
+
+        return result;
+    }
+
+    public PersistentFieldBundle gatherChanges(String pageName)
+    {
+        Collection<PersistentFieldChange> allChanges = CollectionFactory.newList();
+
+        for (PersistentFieldStrategy strategy : _strategies.values())
+        {
+            allChanges.addAll(strategy.gatherFieldChanges(pageName));
+        }
+
+        return new PersistentFieldBundleImpl(allChanges);
+    }
+
+    public void postChange(String pageName, ComponentResources resources, String fieldName,
+            Object newValue)
+    {
+        String strategyName = findStrategy(resources, fieldName);
+        PersistentFieldStrategy strategy = getStrategy(strategyName);
+
+        strategy.postChange(pageName, resources.getNestedId(), fieldName, newValue);
+    }
+
+    private String findStrategy(ComponentResources resources, String fieldName)
+    {
+        ComponentModel model = resources.getComponentModel();
+
+        String strategy = model.getFieldPersistenceStrategy(fieldName);
+
+        if (InternalUtils.isNonBlank(strategy))
+            return strategy;
+
+        // OK, it isn't specified for the field itself, so work up the component hierarchy,
+        // checking to see if any component set a default persistent strategy.
+
+        ComponentResources search = resources;
+        while (search != null)
+        {
+            model = search.getComponentModel();
+
+            strategy = model.getMeta(META_KEY);
+
+            if (strategy != null)
+                return strategy;
+
+            // Work up the containment hierarchy. For a persistent field inside a mixin, the
+            // container is the component. From there we work up the normal page containment
+            // hierarchy.
+
+            search = search.getContainerResources();
+        }
+
+        return DEFAULT_STRATEGY;
+    }
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java Wed Jan 24 14:26:02 2007
@@ -15,6 +15,7 @@
 package org.apache.tapestry.internal.structure;
 
 import org.apache.tapestry.Block;
+import org.apache.tapestry.ComponentResources;
 import org.apache.tapestry.ComponentResourcesCommon;
 import org.apache.tapestry.internal.InternalComponentResources;
 import org.apache.tapestry.internal.InternalComponentResourcesCommon;
@@ -23,6 +24,7 @@
 import org.apache.tapestry.runtime.Component;
 import org.apache.tapestry.runtime.ComponentEvent;
 import org.apache.tapestry.runtime.RenderQueue;
+import org.apache.tapestry.services.PersistentFieldManager;
 
 /**
  * Extended version of {@link org.apache.tapestry.internal.structure.PageElement} for elements that
@@ -112,4 +114,13 @@
      * @return the default binding prefix, or null
      */
     String getDefaultBindingPrefix(String parameterName);
+
+    /**
+     * Posts a change to a persistent field. If the component is still loading, then this change is
+     * ignored. Otherwise, it is propogated, via the
+     * {@link Page#persistFieldChange(ComponentResources, String, Object) page}
+     * to the
+     * {@link PersistentFieldManager#postChange(String, ComponentResources, String, Object) PersistentFieldManager}.
+     */
+    void persistFieldChange(ComponentResources resources, String fieldName, Object newValue);
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java Wed Jan 24 14:26:02 2007
@@ -27,6 +27,7 @@
 import org.apache.tapestry.Block;
 import org.apache.tapestry.BlockNotFoundException;
 import org.apache.tapestry.ComponentEventHandler;
+import org.apache.tapestry.ComponentResources;
 import org.apache.tapestry.Link;
 import org.apache.tapestry.MarkupWriter;
 import org.apache.tapestry.dom.Element;
@@ -537,8 +538,11 @@
         _typeCoercer = typeCoercer;
         _messagesSource = messagesSource;
 
-        _coreResources = new InternalComponentResourcesImpl(this, instantiator, _typeCoercer,
-                _messagesSource);
+        ComponentResources containerResources = container == null ? null : container
+                .getComponentResources();
+
+        _coreResources = new InternalComponentResourcesImpl(this, containerResources, instantiator,
+                _typeCoercer, _messagesSource);
 
         _coreComponent = _coreResources.getComponent();
 
@@ -604,7 +608,7 @@
         String mixinName = IOCUtilities.toSimpleId(mixinClassName);
 
         InternalComponentResourcesImpl resources = new InternalComponentResourcesImpl(this,
-                instantiator, _typeCoercer, _messagesSource);
+                _coreResources, instantiator, _typeCoercer, _messagesSource);
 
         // TODO: Check for name collision?
 
@@ -937,13 +941,13 @@
         return _rendering;
     }
 
-    public void persistFieldChange(String fieldName, Object newValue)
+    public void persistFieldChange(ComponentResources resources, String fieldName, Object newValue)
     {
         // While loading the page (i.e., when setting field defaults), ignore these
         // changes.
 
         if (_loaded)
-            _page.persistFieldChange(this, fieldName, newValue);
+            _page.persistFieldChange(resources, fieldName, newValue);
     }
 
     /** Generate a toString() for the inner classes that represent render phases. */

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/InternalComponentResourcesImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/InternalComponentResourcesImpl.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/InternalComponentResourcesImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/InternalComponentResourcesImpl.java Wed Jan 24 14:26:02 2007
@@ -52,6 +52,8 @@
 
     private final Component _component;
 
+    private final ComponentResources _containerResources;
+
     private Map<String, Binding> _bindings;
 
     private final ComponentMessagesSource _messagesSource;
@@ -59,10 +61,11 @@
     private Messages _messages;
 
     public InternalComponentResourcesImpl(ComponentPageElement element,
-            Instantiator componentInstantiator, TypeCoercer typeCoercer,
-            ComponentMessagesSource messagesSource)
+            ComponentResources containerResources, Instantiator componentInstantiator,
+            TypeCoercer typeCoercer, ComponentMessagesSource messagesSource)
     {
         _element = element;
+        _containerResources = containerResources;
         _componentModel = componentInstantiator.getModel();
         _typeCoercer = typeCoercer;
         _messagesSource = messagesSource;
@@ -155,7 +158,7 @@
 
     public void persistFieldChange(String fieldName, Object newValue)
     {
-        _element.persistFieldChange(fieldName, newValue);
+        _element.persistFieldChange(this, fieldName, newValue);
     }
 
     public void addParameter(String parameterName, Binding binding)
@@ -261,9 +264,7 @@
 
     public ComponentResources getContainerResources()
     {
-        ComponentPageElement containerElement = _element.getContainerElement();
-
-        return containerElement == null ? null : containerElement.getComponentResources();
+        return _containerResources;
     }
 
     public Locale getLocale()

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java Wed Jan 24 14:26:02 2007
@@ -17,6 +17,7 @@
 import java.util.Locale;
 
 import org.apache.commons.logging.Log;
+import org.apache.tapestry.ComponentResources;
 import org.apache.tapestry.Link;
 import org.apache.tapestry.runtime.Component;
 import org.apache.tapestry.runtime.PageLifecycleListener;
@@ -120,20 +121,21 @@
      * @param context
      * @return
      */
-    Link createActionLink(ComponentPageElement element, String action,
-            boolean forForm, Object... context);
+    Link createActionLink(ComponentPageElement element, String action, boolean forForm,
+            Object... context);
 
     /**
      * Posts a change to a persistent field.
      * 
-     * @param element
-     *            the component element containing the field
+     * @param resources
+     *            the component resources for the component or mixin containing the field whose
+     *            value changed
      * @param fieldName
      *            the name of the field
      * @param newValue
      *            the new value for the field
      */
-    void persistFieldChange(ComponentPageElement element, String fieldName, Object newValue);
+    void persistFieldChange(ComponentResources resources, String fieldName, Object newValue);
 
     /**
      * Gets a change for a field within the component.

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java Wed Jan 24 14:26:02 2007
@@ -20,6 +20,7 @@
 import java.util.Locale;
 
 import org.apache.commons.logging.Log;
+import org.apache.tapestry.ComponentResources;
 import org.apache.tapestry.Link;
 import org.apache.tapestry.internal.services.LinkFactory;
 import org.apache.tapestry.runtime.Component;
@@ -135,7 +136,7 @@
     public void attached()
     {
         if (_dirtyCount != 0)
-            throw new IllegalStateException("Page was stored into the page pool in a dirty state.");
+            throw new IllegalStateException(StructureMessages.pageIsDirty(this));
 
         for (PageLifecycleListener listener : _listeners)
             listener.containingPageDidAttach();
@@ -152,14 +153,9 @@
         return _linkFactory.createActionLink(element, action, forForm, context);
     }
 
-    public void persistFieldChange(ComponentPageElement element, String fieldName, Object newValue)
+    public void persistFieldChange(ComponentResources resources, String fieldName, Object newValue)
     {
-        String strategy = element.getComponentResources().getComponentModel()
-                .getFieldPersistenceStrategy(fieldName);
-
-        String componentId = element.getNestedId();
-
-        _persistentFieldManager.postChange(_name, componentId, fieldName, strategy, newValue);
+        _persistentFieldManager.postChange(_name, resources, fieldName, newValue);
     }
 
     public Object getFieldChange(ComponentPageElement element, String fieldName)

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StructureMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StructureMessages.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StructureMessages.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/StructureMessages.java Wed Jan 24 14:26:02 2007
@@ -80,4 +80,9 @@
     {
         return MESSAGES.format("unbalanced-elements", componentId);
     }
+
+    static String pageIsDirty(Page page)
+    {
+        return MESSAGES.format("page-is-dirty", page);
+    }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java Wed Jan 24 14:26:02 2007
@@ -24,6 +24,7 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.ComponentResourcesCommon;
 import org.apache.tapestry.Link;
 import org.apache.tapestry.events.InvalidationListener;
 import org.apache.tapestry.internal.InternalComponentResources;
@@ -197,14 +198,14 @@
         expect(element.getComponent()).andReturn(component);
     }
 
-    protected final void train_getId(ComponentPageElement childElement, String id)
+    protected final void train_getId(ComponentResourcesCommon resources, String id)
     {
-        expect(childElement.getId()).andReturn(id).atLeastOnce();
+        expect(resources.getId()).andReturn(id).atLeastOnce();
     }
 
-    protected final void train_getNestedId(ComponentPageElement element, String nestedId)
+    protected final void train_getNestedId(ComponentResourcesCommon resources, String nestedId)
     {
-        expect(element.getNestedId()).andReturn(nestedId).atLeastOnce();
+        expect(resources.getNestedId()).andReturn(nestedId).atLeastOnce();
     }
 
     protected final void train_getContextPath(Request request, String contextPath)
@@ -318,9 +319,9 @@
         expect(templateSource.getTemplate(model, locale)).andReturn(template);
     }
 
-    protected final void train_getComponentModel(ComponentResources component, ComponentModel model)
+    protected final void train_getComponentModel(ComponentResources resources, ComponentModel model)
     {
-        expect(component.getComponentModel()).andReturn(model);
+        expect(resources.getComponentModel()).andReturn(model).atLeastOnce();
     }
 
     protected final void train_newRootComponentElement(PageElementFactory elementFactory,
@@ -463,7 +464,8 @@
         return newMock(FormParameterLookup.class);
     }
 
-    protected final void train_getParameter(FormParameterLookup lookup, String elementName, String value)
+    protected final void train_getParameter(FormParameterLookup lookup, String elementName,
+            String value)
     {
         expect(lookup.getParameter(elementName)).andReturn(value).atLeastOnce();
     }

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=499593&r1=499592&r2=499593
==============================================================================
--- 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 Wed Jan 24 14:26:02 2007
@@ -60,7 +60,7 @@
      * Returns the persistent strategy associated with the field.
      * 
      * @param fieldName
-     * @return the corresponding strategy
+     * @return the corresponding strategy, or the empty string
      * @throw IllegalArgumentException if the named field is not marked as persistent
      */
     String getFieldPersistenceStrategy(String fieldName);
@@ -132,4 +132,14 @@
      * @return true if the mixin should operate after, not before, the component
      */
     boolean isMixinAfter();
+
+    /**
+     * Gets a meta value identified by the given key. If the current model does not provide a value
+     * for the key, then the parent component model (if any) is searched.
+     * 
+     * @param key
+     *            identifies the value to be accessed
+     * @return the value for the key (possibly inherited from a parent model), or null
+     */
+    String getMeta(String key);
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableComponentModel.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableComponentModel.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableComponentModel.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableComponentModel.java Wed Jan 24 14:26:02 2007
@@ -62,7 +62,9 @@
      * @param fieldName
      *            the name of the field which is to be made persistent
      * @param strategy
-     *            the strategy for persisting the field, from {@link Persist#value()}
+     *            the strategy for persisting the field, from {@link Persist#value()}. This value
+     *            may be blank, in which case the stategy is inherited from the component, or the
+     *            component's container.
      * @return a logical name for the field, to be used with
      *         {@link ComponentModel#getFieldPersistenceStrategy(String)}, and with
      *         {@link InternalComponentResources#persistFieldChange(String, Object)}, etc.
@@ -80,4 +82,7 @@
 
     /** Changes the value of the mixinAfter flag. The default value is false. */
     void setMixinAfter(boolean mixinAfter);
+
+    /** Stores a meta data value under the indicated key. */
+    void setMeta(String key, String value);
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/Cookies.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/Cookies.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/Cookies.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/Cookies.java Wed Jan 24 14:26:02 2007
@@ -16,9 +16,6 @@
 
 /**
  * Used by other services to obtain cookie values for the current request.
- * 
- * @author Howard Lewis Ship
- * @since 4.0
  */
 public interface Cookies
 {
@@ -50,31 +47,27 @@
      */
 
     void writeCookieValue(String name, String value, int maxAge);
-    
+
     /**
-     * As with {@link #writeCookieValue(String, String)} but an explicit path
-     * may be set.
+     * As with {@link #writeCookieValue(String, String)} but an explicit path may be set.
      */
     void writeCookieValue(String name, String value, String path);
-    
+
     /**
-     * As with {@link #writeCookieValue(String, String)} but an explicit path
-     * may be set.
+     * As with {@link #writeCookieValue(String, String)} but an explicit path may be set.
      */
     void writeDomainCookieValue(String name, String value, String domain);
-    
+
     /**
-     * As with {@link #writeCookieValue(String, String)} but an explicit path
-     * may be set.
+     * As with {@link #writeCookieValue(String, String)} but an explicit path may be set.
      */
     void writeDomainCookieValue(String name, String value, String domain, int maxAge);
-    
+
     /**
-     * As with {@link #writeCookieValue(String, String, String)} but an explicit
-     * domain may be set.
+     * As with {@link #writeCookieValue(String, String, String)} but an explicit domain may be set.
      */
     void writeCookieValue(String name, String value, String path, String domain);
-    
+
     /**
      * Removes a previously written cookie, by writing a new cookie with a maxAge of 0.
      */

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldManager.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldManager.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldManager.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldManager.java Wed Jan 24 14:26:02 2007
@@ -12,38 +12,38 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.services;
-
-/**
- * Handle persistent property changes. Primarily, delegates to a number of
- * {@link PersistentFieldStrategy} instances.
- */
-public interface PersistentFieldManager
-{
-    /**
-     * Posts a change of a persistent property.
-     * 
-     * @param pageName
-     *            the name of the page containing the component
-     * @param componentId
-     *            the nested id path of the component (or null for the page's root component)
-     * @param fieldName
-     *            the name of the field whose persistent value has changed
-     * @param strategy
-     *            the name of the stategy used to persist the property value
-     * @param newValue
-     *            the new value for the field, possibly null
-     */
-    void postChange(String pageName, String componentId, String fieldName, String strategy,
-            Object newValue);
-
-    /**
-     * Locates all persistently stored changes to all properties within the page (for the current
-     * session and request) and gathers them together into a bundle.
-     * 
-     * @param pageName
-     *            the name of the page to gather changes for
-     * @return a bundle identifying all such changes
-     */
-    PersistentFieldBundle gatherChanges(String pageName);
-}
+package org.apache.tapestry.services;
+
+import org.apache.tapestry.ComponentResources;
+
+/**
+ * Handle persistent property changes. Primarily, delegates to a number of
+ * {@link PersistentFieldStrategy} instances.
+ */
+public interface PersistentFieldManager
+{
+    /**
+     * Posts a change of a persistent property.
+     * 
+     * @param pageName
+     *            the name of the page containing the component
+     * @param resources
+     *            the resources for the component or mixin (used to determine the persistence
+     *            strategy)
+     * @param fieldName
+     *            the name of the field whose persistent value has changed
+     * @param newValue
+     *            the new value for the field, possibly null
+     */
+    void postChange(String pageName, ComponentResources resources, String fieldName, Object newValue);
+
+    /**
+     * Locates all persistently stored changes to all properties within the page (for the current
+     * session and request) and gathers them together into a bundle.
+     * 
+     * @param pageName
+     *            the name of the page to gather changes for
+     * @return a bundle identifying all such changes
+     */
+    PersistentFieldBundle gatherChanges(String pageName);
+}

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=499593&r1=499592&r2=499593
==============================================================================
--- 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 Wed Jan 24 14:26:02 2007
@@ -22,7 +22,6 @@
 import java.util.Map;
 
 import javax.servlet.ServletContext;
-import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
@@ -70,6 +69,9 @@
 import org.apache.tapestry.internal.services.ComponentWorker;
 import org.apache.tapestry.internal.services.ContextImpl;
 import org.apache.tapestry.internal.services.ContextPathSource;
+import org.apache.tapestry.internal.services.CookieSink;
+import org.apache.tapestry.internal.services.CookieSource;
+import org.apache.tapestry.internal.services.CookiesImpl;
 import org.apache.tapestry.internal.services.DefaultInjectionProvider;
 import org.apache.tapestry.internal.services.DefaultValidationDelegateCommand;
 import org.apache.tapestry.internal.services.EnvironmentImpl;
@@ -89,6 +91,7 @@
 import org.apache.tapestry.internal.services.LinkActionResponseGenerator;
 import org.apache.tapestry.internal.services.LinkFactory;
 import org.apache.tapestry.internal.services.MarkupWriterImpl;
+import org.apache.tapestry.internal.services.MetaWorker;
 import org.apache.tapestry.internal.services.MixinAfterWorker;
 import org.apache.tapestry.internal.services.MixinWorker;
 import org.apache.tapestry.internal.services.ObjectComponentEventResultProcessor;
@@ -635,6 +638,7 @@
      * annotation</li>
      * <li>InjectBlock -- allows a block from the template to be injected into a field</li>
      * <li>SupportsInformalParameters -- checks for the annotation</li>
+     * <li>Meta -- checks for meta data and adds it to the component model
      * <li>UnclaimedField -- identifies unclaimed fields and resets them to null/0/false at the end
      * of the request</li>
      * <li>RenderCommand -- ensures all components also implement {@link RenderCommand}</li>
@@ -656,6 +660,7 @@
         // only have a single annotation, the order doesn't matter so much, as long as
         // UnclaimedField is last.
 
+        configuration.add("Meta", new MetaWorker());
         configuration.add("InjectNamed", new InjectNamedWorker(objectProvider, locator));
         configuration.add(
                 "InjectAnonymous",
@@ -1077,37 +1082,12 @@
     }
 
     public static Cookies buildCookies(@InjectService("tapestry.internal.ContextPathSource")
-    ContextPathSource contextPathSource, @InjectService("CookieSource")
-    CookieSource cookieSource, @InjectService("CookieSink")
+    ContextPathSource contextPathSource, @InjectService("tapestry.internal.CookieSource")
+    CookieSource cookieSource, @InjectService("tapestry.internal.CookieSink")
     CookieSink cookieSink, @Inject("${tapestry.default-cookie-max-age}")
     int defaultMaxAge)
     {
         return new CookiesImpl(contextPathSource, cookieSource, cookieSink, defaultMaxAge);
     }
 
-    public CookieSource buildCookieSource()
-    {
-        return new CookieSource()
-        {
-
-            public Cookie[] getCookies()
-            {
-                return _requestGlobals.getHTTPServletRequest().getCookies();
-            }
-
-        };
-    }
-
-    public CookieSink buildCookieSink()
-    {
-        return new CookieSink()
-        {
-
-            public void addCookie(Cookie cookie)
-            {
-                _requestGlobals.getHTTPServletResponse().addCookie(cookie);
-            }
-
-        };
-    }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java Wed Jan 24 14:26:02 2007
@@ -740,4 +740,9 @@
     {
         expect(validator.skipIfBlank()).andReturn(skipIfBlank).atLeastOnce();
     }
+
+    protected final void train_getFieldPersistenceStrategy(ComponentModel model, String fieldName, String fieldStrategy)
+    {
+        expect(model.getFieldPersistenceStrategy(fieldName)).andReturn(fieldStrategy).atLeastOnce();
+    }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/pagelevel/CookiesForPageTester.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/pagelevel/CookiesForPageTester.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/pagelevel/CookiesForPageTester.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/pagelevel/CookiesForPageTester.java Wed Jan 24 14:26:02 2007
@@ -18,9 +18,9 @@
 
 import javax.servlet.http.Cookie;
 
+import org.apache.tapestry.internal.services.CookieSink;
+import org.apache.tapestry.internal.services.CookieSource;
 import org.apache.tapestry.ioc.internal.util.CollectionFactory;
-import org.apache.tapestry.services.CookieSink;
-import org.apache.tapestry.services.CookieSource;
 
 public class CookiesForPageTester implements CookieSource, CookieSink
 {

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/pagelevel/PageTester.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/pagelevel/PageTester.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/pagelevel/PageTester.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/pagelevel/PageTester.java Wed Jan 24 14:26:02 2007
@@ -147,8 +147,9 @@
                 "tapestry.internal.FormParameterLookup",
                 _formParameterLookup);
         addDefaultOverride(modifiedOverrides, "tapestry.internal.SessionHolder", _sessionHolder);
-        addDefaultOverride(modifiedOverrides, "tapestry.CookieSource", _cookies);
-        addDefaultOverride(modifiedOverrides, "tapestry.CookieSink", _cookies);
+        addDefaultOverride(modifiedOverrides, "tapestry.internal.CookieSource", _cookies);
+        addDefaultOverride(modifiedOverrides, "tapestry.internal.CookieSink", _cookies);
+        
         return modifiedOverrides;
     }
 
@@ -323,5 +324,4 @@
     {
         _preferedLanguage = preferedLanguage;
     }
-
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/InternalStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/InternalStrings.properties?view=auto&rev=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/InternalStrings.properties (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/InternalStrings.properties Wed Jan 24 14:26:02 2007
@@ -0,0 +1,2 @@
+bad-key-value=Key/value pair '%s' is not properly formatted (it does not contain a equals sign).
+ 
\ No newline at end of file

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/structure/StructureStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/structure/StructureStrings.properties?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/structure/StructureStrings.properties (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/structure/StructureStrings.properties Wed Jan 24 14:26:02 2007
@@ -26,4 +26,6 @@
 block-not-found=Template for component %s does not contain a block with identifier '%s'.
 unbalanced-elements=Component %s has rendered unbalanced elements; \
   either it has started an element with MarkupWriter.element() and not followed up with MarkupWriter.end(), or \
-  it has invoked MarkupWriter.end() without first invoking MarkupWriter.element().
\ No newline at end of file
+  it has invoked MarkupWriter.end() without first invoking MarkupWriter.element().
+page-is-dirty=Page %s was stored into the page pool in a dirty state. This should never happen, \
+  and may indicate that a reference to the page (or component within the page) was retained past the end of a request.

Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/persist.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/persist.apt?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/persist.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/persist.apt Wed Jan 24 14:26:02 2007
@@ -21,9 +21,7 @@
   @Persist
   private int _value;
 +----+
-  
-
-  
+    
   Annotated fields will store their state between requests. Generally, speaking, this means
   that the value is stored into the session (but other approaches are possible).
   
@@ -44,6 +42,21 @@
   
   A suitably long session attribute name is used; it incorporates the
   name of the page, the nested component id, and the name of the field.
+  
+Persistence Search
+
+  By default the value for the Persist annotation is the empty string. When this is true,
+  then the actual strategy to be used is determined by a search up the 
+  component hiearchy.
+  
+  For each component, the meta-data property <<<tapestry.persistence-strategy>>> is checked.
+  This can be specified using the 
+  {{{../apidocs/org/apache/tapestry/annotations/ComponentClass.html}ComponentClass}} annotation.
+  If the value is non-blank, then that strategy is used. This allows a component to control
+  the persistence strategy used inside any sub-components (that don't explicitly use a different
+  strategy).
+  
+  In any case, if no component provides the meta data, then the ultimate default, "session", is used.  
   
 Default Values
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/TapestryUtilsTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/TapestryUtilsTest.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/TapestryUtilsTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/TapestryUtilsTest.java Wed Jan 24 14:26:02 2007
@@ -175,4 +175,38 @@
 
         // Waste of effort to re-test each individual option model.
     }
+
+    @Test
+    public void parse_key_value()
+    {
+        KeyValue kv = TapestryUtils.parseKeyValue("foo=bar");
+
+        assertEquals(kv.getKey(), "foo");
+        assertEquals(kv.getValue(), "bar");
+    }
+
+    @Test
+    public void bad_format_key_value_pair()
+    {
+        String input = "abraxas";
+
+        try
+        {
+            TapestryUtils.parseKeyValue(input);
+            unreachable();
+        }
+        catch (IllegalArgumentException ex)
+        {
+            assertEquals(ex.getMessage(), InternalMessages.badKeyValue(input));
+        }
+    }
+
+    @Test
+    public void whitespace_trimmed_for_key_value()
+    {
+        KeyValue kv = TapestryUtils.parseKeyValue("  mykey = myvalue ");
+
+        assertEquals(kv.getKey(), "mykey");
+        assertEquals(kv.getValue(), "myvalue");
+    }
 }

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=499593&r1=499592&r2=499593
==============================================================================
--- 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 Wed Jan 24 14:26:02 2007
@@ -622,4 +622,78 @@
         verify();
     }
 
+    @Test
+    public void set_and_get_meta()
+    {
+        Resource r = newResource();
+        Log log = newLog();
+
+        replay();
+
+        MutableComponentModel model = new MutableComponentModelImpl(CLASS_NAME, log, r, null);
+
+        model.setMeta("fred", "flintstone");
+        model.setMeta("barney", "rubble");
+
+        assertEquals(model.getMeta("fred"), "flintstone");
+        assertEquals(model.getMeta("barney"), "rubble");
+
+        verify();
+    }
+
+    @Test
+    public void get_meta_from_parent()
+    {
+        Resource r = newResource();
+        Log log = newLog();
+
+        replay();
+
+        MutableComponentModel parent = new MutableComponentModelImpl(CLASS_NAME, log, r, null);
+        MutableComponentModel child = new MutableComponentModelImpl(CLASS_NAME, log, r, parent);
+
+        parent.setMeta("fred", "flintstone");
+
+        assertEquals(child.getMeta("fred"), "flintstone");
+
+        verify();
+    }
+
+    @Test
+    public void parent_does_not_have_meta()
+    {
+        Resource r = newResource();
+        Log log = newLog();
+
+        replay();
+
+        MutableComponentModel parent = new MutableComponentModelImpl(CLASS_NAME, log, r, null);
+        MutableComponentModel child = new MutableComponentModelImpl(CLASS_NAME, log, r, parent);
+
+        parent.setMeta("fred", "flintstone");
+
+        assertNull(child.getMeta("wilma"));
+
+        verify();
+    }
+
+    @Test
+    public void child_meta_overrides_parent_meta()
+    {
+        Resource r = newResource();
+        Log log = newLog();
+
+        replay();
+
+        MutableComponentModel parent = new MutableComponentModelImpl(CLASS_NAME, log, r, null);
+        MutableComponentModel child = new MutableComponentModelImpl(CLASS_NAME, log, r, parent);
+
+        parent.setMeta("fred", "flintstone");
+        child.setMeta("fred", "mcmurray");
+
+        assertEquals(parent.getMeta("fred"), "flintstone");
+        assertEquals(child.getMeta("fred"), "mcmurray");
+
+        verify();
+    }
 }

Copied: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/CookiesImplTest.java (from r497292, tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/services/CookiesImplTest.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/CookiesImplTest.java?view=diff&rev=499593&p1=tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/services/CookiesImplTest.java&r1=497292&p2=tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/CookiesImplTest.java&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/services/CookiesImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/CookiesImplTest.java Wed Jan 24 14:26:02 2007
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.services;
+package org.apache.tapestry.internal.services;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -20,6 +20,9 @@
 import javax.servlet.http.Cookie;
 
 import org.apache.tapestry.internal.services.ContextPathSource;
+import org.apache.tapestry.internal.services.CookieSink;
+import org.apache.tapestry.internal.services.CookieSource;
+import org.apache.tapestry.internal.services.CookiesImpl;
 import org.apache.tapestry.ioc.internal.util.CollectionFactory;
 import org.testng.Assert;
 import org.testng.annotations.Test;
@@ -28,7 +31,6 @@
  * Tests for {@link org.apache.tapestry.services.impl.CookiesImpl}.
  * 
  * @author Howard Lewis Ship
- * @since 4.0
  */
 @Test
 public class CookiesImplTest extends Assert

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/InternalClassTransformationImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/InternalClassTransformationImplTest.java?view=diff&rev=499593&r1=499592&r2=499593
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/InternalClassTransformationImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/InternalClassTransformationImplTest.java Wed Jan 24 14:26:02 2007
@@ -39,6 +39,7 @@
 import org.apache.tapestry.annotations.SetupRender;
 import org.apache.tapestry.internal.InternalComponentResources;
 import org.apache.tapestry.internal.test.InternalBaseTestCase;
+import org.apache.tapestry.internal.transform.InheritedAnnotation;
 import org.apache.tapestry.internal.transform.pages.AbstractFoo;
 import org.apache.tapestry.internal.transform.pages.BarImpl;
 import org.apache.tapestry.internal.transform.pages.BasicComponent;
@@ -387,7 +388,7 @@
     public void ensure_subclasses_inherit_parent_class_annotations() throws Exception
     {
         // The Java runtime does honor @Inherited
-        assertNotNull(ChildClassInheritsAnnotation.class.getAnnotation(ComponentClass.class));
+        assertNotNull(ChildClassInheritsAnnotation.class.getAnnotation(InheritedAnnotation.class));
 
         Log log = newLog();
 
@@ -395,11 +396,11 @@
 
         ClassTransformation ct = createClassTransformation(ChildClassInheritsAnnotation.class, log);
 
-        ComponentClass cc = ct.getAnnotation(ComponentClass.class);
+        InheritedAnnotation ia = ct.getAnnotation(InheritedAnnotation.class);
 
         // Javassist does not, but ClassTransformation patches around that.
 
-        assertNotNull(cc);
+        assertNotNull(ia);
 
         verify();
     }