You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by ro...@apache.org on 2009/07/18 00:53:32 UTC
svn commit: r795264 [1/3] - in /tapestry/tapestry5/trunk: src/site/apt/dev/
src/site/apt/guide/
tapestry-core/src/main/java/org/apache/tapestry5/annotations/
tapestry-core/src/main/java/org/apache/tapestry5/internal/
tapestry-core/src/main/java/org/apa...
Author: robertdzeigler
Date: Fri Jul 17 22:53:30 2009
New Revision: 795264
URL: http://svn.apache.org/viewvc?rev=795264&view=rev
Log:
TAP5-103: provide access to component parameters from within mixins
TAP5-748: NPE when defining a component using just t:id and no type or @Component annotation
TAP5-777: Tapestry should ensure that mixins are applied in a deterministic order.
TAP5-786: Exception messages in the pageload packages should be localized
TAP5-787: AbstractIntegrationTest's "assertTextPresent" only checks the first value
Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/BindParameter.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterChangeListener.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterChangedEvent.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageloadMessages.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/BindParameterWorker.java
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/pageload/
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/pageload/PageloadStrings.properties
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/BindParameterComponent.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/BindParameterComponentContainer.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/TextOnlyOnDisabledTextField.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/mixins/EchoAfter.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/mixins/EchoAfter2.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/mixins/EchoValue.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/mixins/EchoValue2.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/mixins/EchoValue3.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/mixins/TextOnlyOnDisabled.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/BindParameterDemo.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/BindParameterNoSuchParameter.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/BindParameterOnComponent.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/MissingComponentClassException.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/MixinOrderingDemo.java
tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/BindParameterComponentContainer.tml
tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/BindParameterDemo.tml
- copied, changed from r786676, tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/ParameterDefault.tml
tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/BindParameterNoSuchParameter.tml
tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/BindParameterOnComponent.tml
tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/MissingComponentClassException.tml
- copied, changed from r786676, tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/BadTemplate.tml
tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/MixinOrderingDemo.tml
Modified:
tapestry/tapestry5/trunk/src/site/apt/dev/bible.apt
tapestry/tapestry5/trunk/src/site/apt/guide/mixins.apt
tapestry/tapestry5/trunk/src/site/apt/guide/rendering.apt
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Mixin.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/MixinClasses.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Mixins.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResources.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResourcesCommon.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterAccess.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableComponentModelImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableEmbeddedComponentModelImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/ParameterModelImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentAssemblerImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/CompositeRenderCommand.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/EmbeddedComponentAssemblerImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageAssembly.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageLoaderImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/TokenStreamImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/parser/ExtensionPointToken.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/StaxTemplateParser.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElement.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StructureMessages.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ComponentWorker.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/MixinWorker.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/TransformMessages.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/model/ComponentModel.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/model/EmbeddedComponentModel.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/model/MutableComponentModel.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/model/MutableEmbeddedComponentModel.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/model/ParameterModel.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/structure/StructureStrings.properties
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/transform/TransformStrings.properties
tapestry/tapestry5/trunk/tapestry-core/src/test/app1/ClientPersistenceDemo.tml
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/IntegrationTests.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ClientPersistenceDemo.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/TemplateParserImplTest.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/transform/MixinWorkerTest.java
tapestry/tapestry5/trunk/tapestry-test/src/main/java/org/apache/tapestry5/test/AbstractIntegrationTestSuite.java
Modified: tapestry/tapestry5/trunk/src/site/apt/dev/bible.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/apt/dev/bible.apt?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/apt/dev/bible.apt (original)
+++ tapestry/tapestry5/trunk/src/site/apt/dev/bible.apt Fri Jul 17 22:53:30 2009
@@ -122,7 +122,7 @@
Evolving Interfaces
- Tapestry users interfaces quite extensively.
+ Tapestry uses interfaces quite extensively.
Interfaces fall into two categories: service interfaces called by user code, and interfaces implemented by user code.
Modified: tapestry/tapestry5/trunk/src/site/apt/guide/mixins.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/apt/guide/mixins.apt?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/apt/guide/mixins.apt (original)
+++ tapestry/tapestry5/trunk/src/site/apt/guide/mixins.apt Fri Jul 17 22:53:30 2009
@@ -93,7 +93,7 @@
attribute of the annotation will be used to determine the mixin class name:
+---+
-public class AutocompleteField extendes TextField
+public class AutocompleteField extends TextField
{
@Mixin("Autocomplete")
private Object autocompleteMixin;
@@ -123,7 +123,76 @@
private TextField userId;
+-----+
+Binding the parameter of the core component
+
+ It is sometimes desirable to access the current value of a parameter defined in the component associated with the
+ mixin. For example: normally, when the textfield component is marked disabled, it renders a text field with a disabled
+ attribute, but you want it to output the (plaintext) value when disabled. A mixin for this purpose would need access
+ to at least the disabled, and value parameters, and possibly the translate parameter (for a client-side representation).
+ You can access the disabled parameter via @InjectContainer and checking isDisabled on the field, but textfield
+ currently provides no access to value or translate. In this case, you can bind the core-component parameter using the
+ @BindParameter annotation:
+
++-----+
+ public class MyMixin
+ {
+ @BindParameter
+ private boolean disabled;
+
+ @BindParameter
+ private FieldTranslator translate;
+
+ @BindParameter
+ private Object value;
+
+ Boolean beginRender(MarkupWriter writer)
+ {
+ ...
+ if (disabled)
+ {
+ ...
+ String stringValue = translate.toClient(value));
+ ...
+ }
+ ...
+ }
+ ....
++-----+
+
+ Tapestry will "link" the disabled, translate, and value fields above to parameters of the same name on the
+ associated component. The fields are not parameters to the mixin, but local copies
+ of the component parameter. They are cached only if the associated component parameter is cached. They are read-write,
+ and Tapestry handles synchronizing the value between mixins and the associated component such that even with a cached
+ parameter, components and mixins will share the same value for a given parameter/bound-parameter during render.
+ Only declared parameters of the associated components may be bound.
+ By default, Tapestry will bind the parameter with the same name as the field. You can explicitly declare the parameter
+ to bind via the value attribute:
+
++-----+
+ @BindParameter("translate")
+ private FieldTranslator translator;
++-----+
+
+ In same cases, a mixin will be used on different components using different names for a similar parameter type.
+ For instance, BeanEditor has an "object" parameter; most form fields have a "value" parameter, and Grid has a
+ "source" parameter. These parameters have different names but share the feature of being the "principle" parameter
+ on which the components are acting. A mixin useable by all three components can specify multiple potential
+ parameter values to bind. The first value that matches a declared parameter of the associated component will be used:
+
++-----+
+public class MyMixin
+{
+ ...
+ @BindParameter({"value","object","source"})
+ private Object principalObject;
+ ...
+}
++-----+
+
+ "MyMixin" can be used on a textfield (principalObject is bound to "value"), on BeanEditor or BeanDisplay
+ (principalObject is bound to "object"), or on Grid or Loop (principalObject is bound to "source").
+
Render Phase Ordering
All mixins for a component execute their render phase methods <before> the component's render phase
@@ -132,5 +201,44 @@
Exception: Mixins whose class is annotated with
{{{../apidocs/org/apache/tapestry5/annotations/MixinAfter.html}MixinAfter}} are ordered
- <after> the component, not before.
-
\ No newline at end of file
+ <after> the component, not before.
+
+ Withing a given phase and class (@MixinAfter vs. mixin before), mixin ordering is determined by the ordering
+ constraints specified in the mixin definitions. The constraint definitions follow the same conventions as
+ {{{../tapestry-ioc/order.html}ordered service configurations}}. How you specify the constraints depends
+ on how the mixin is specified.
+
+ Implementation Mixin:
+
++-----+
+ @Mixin("Autocomplete",order={"before:DiscardBody","after:RenderDisabled"}
+ private TextField userId;
++-----+
+
+ Template-specified instance mixins:
+
++-----+
+ \<input t:id="myfield" t:mixins="autocomplete::before:discardbody;after:renderdisabled,
+ defaultfromcookie::before:autocomplete"/\>
++-----+
+
+ @Mixins-specified instance mixins:
+
++-----+
+ @Component(...)
+ @Mixins("Autocomplete::before:discardbody;after:renderdisabled","DefaultFromCookie::before:autocomplete"))
+ private TextField userId;
++-----+
+
+ @MixinClasses-specified instance mixins:
+
++-----+
+ @Component(...)
+ @MixinClasses(value={Autocomplete.class,DefaultFromCookie.class},
+ order={"before:discardbody;after:renderdisabled","before:autocomplete")
++-----+
+
+ The ordering is always specified in terms of the order of the "forward" rendering process (setupRender,
+ beginRender, etc.). When the "reverse" rendering phases (afterRender, etc.) occur, the mixin order is exactly reversed.
+ Mixins which have no associated ordering constraints will be ordered in a manner which is consistent with the
+ specified constraints for all other mixins, but is otherwise unspecified.
Modified: tapestry/tapestry5/trunk/src/site/apt/guide/rendering.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/apt/guide/rendering.apt?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/apt/guide/rendering.apt (original)
+++ tapestry/tapestry5/trunk/src/site/apt/guide/rendering.apt Fri Jul 17 22:53:30 2009
@@ -289,12 +289,14 @@
the component's render phase methods. If a mixin extends from a base class, the mixin's
parent class methods execute before the mixin subclass' render phase methods.
- The order in which the mixins execute is not defined at this time.
-
Exception: Mixins whose class is annotated with
{{{../apidocs/org/apache/tapestry5/annotations/MixinAfter.html}MixinAfter}} are ordered
<after> the component, not before.
-
+
+ The order in which the mixins of a given class (@MixinAfter or mixins before) execute is determined
+ by the ordering constraints specified for the mixins. If no constrains are provided, the order is undefined.
+ See {{{mixins.html}component mixins} for more details.
+
* Parents before Child
Ordering is always parent-first. Methods defined in the parent class are always invoked
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/BindParameter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/BindParameter.java?rev=795264&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/BindParameter.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/BindParameter.java Fri Jul 17 22:53:30 2009
@@ -0,0 +1,49 @@
+// Copyright 2009 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.tapestry5.annotations;
+
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Designates a field in a mixin which is bound to the parameter of the containing
+ * component corresponding to the value of the annotation. If no value is specified,
+ * the bound parameter name is assumed to match the field name of the mixin.
+ *
+ * For example, a mixin intended to work with form fields would define a field named
+ * "value", marked by this annotation. The user-variable bound to the component's value
+ * parameter would ultimately be bound in a chain:
+ * uservariable <=> mixin.value <=> component.value.
+ * Changes to any one value in the chain will be propagated accordingly.
+ *
+ * @since 5.2.0.0
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface BindParameter
+{
+
+ /**
+ * @return the name of the mixin bound-parameter, exactly as for the Parameter annotation.
+ */
+ String name() default "";
+ /**
+ * @return the name(s) of the parent parameter to bind. Defaults to the name of the mixin field. If more than one
+ * name is specified, the first name matching a declared parameter of the core component will be used.
+ */
+ String[] value() default {""};
+}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Mixin.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Mixin.java?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Mixin.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Mixin.java Fri Jul 17 22:53:30 2009
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007 The Apache Software Foundation
+// Copyright 2006, 2007, 2009 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.
@@ -36,4 +36,12 @@
* the mixin class to instantiate and attach to the component.
*/
String value() default "";
+
+ /**
+ * Defines an ordering constraint for when the mixin should be applied in relation to other mixins.
+ * The string is analagous exactly to the strings used to define ordered contributions.
+ * Eg: @Mixin(order={"before:mixina","after:mixinb"}). The ids are mixin names and are case insensitive.
+ * @since 5.2.0.0
+ */
+ String[] order() default {};
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/MixinClasses.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/MixinClasses.java?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/MixinClasses.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/MixinClasses.java Fri Jul 17 22:53:30 2009
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2009 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.
@@ -32,4 +32,15 @@
public @interface MixinClasses
{
Class[] value();
+
+ /**
+ * Specifies the ordering constraints for each mixin specified by value. Order may be a 0-length array signifying
+ * no ordering for any mixin specified by value. Otherwise, it must be the same length as value.
+ * Each String can specify multiple constraints, separated by ; (before:mixina;after:mixinb).
+ * Alternatively, the entry may be null or the empty string to specify no ordering constraints for that particular
+ * mixin.
+ * @since 5.2.0.0
+ */
+ String[] order() default {};
+
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Mixins.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Mixins.java?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Mixins.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Mixins.java Fri Jul 17 22:53:30 2009
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2009 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.
@@ -33,7 +33,11 @@
{
/**
- * One or more mixin type names, from which actual mixin class names can be resolved.
+ * One or more mixin type names and optional constraints, from which actual mixin class names can be resolved.
+ * For specifying constraints, do:
+ * mixinname::constraint1;constraint2
+ * For instance:
+ * mixina::before:mixinb;after:mixinc;after:mixind
*/
String[] value();
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResources.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResources.java?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResources.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResources.java Fri Jul 17 22:53:30 2009
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009 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.
@@ -62,4 +62,14 @@
* @return object used to read and update the parameter
*/
ParameterAccess getParameterAccess(String parameterName);
+
+ /**
+ * Gets access object suitable for handling mixin fields which are bound to a parameter of the core component
+ * @param boundParameterName the name of the mixin field that should be linked to the core component's field.
+ * @param parentParameterNames the list of parameter names to try in the parent. The first name that matches a
+ * declared parameter name in the core component will be used. This allows BindParameter to be used with
+ * mixins that have a similar parameter type with different parameter names (eg:
+ * @since 5.2.0.0
+ */
+ ParameterAccess getContainerBoundParameterAccess(String boundParameterName, String... parentParameterNames);
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResourcesCommon.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResourcesCommon.java?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResourcesCommon.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResourcesCommon.java Fri Jul 17 22:53:30 2009
@@ -57,6 +57,19 @@
Component getMixinByClassName(String mixinClassName);
/**
+ * Returns true if the mixin named by mixinClassName is associated with the core component
+ * @param mixinClassName fully qualified class name
+ * @since 5.2.0.0
+ */
+ boolean isMixingIn(String mixinClassName);
+
+ /**
+ * Registers an action that should be executed immediately after page load.
+ * @since 5.2.0.0
+ */
+ void deferLoadAction(Runnable action);
+
+ /**
* Constructs a map linking informal parameters to the corresponding bindings.
*
* @return map, possible empty
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterAccess.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterAccess.java?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterAccess.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterAccess.java Fri Jul 17 22:53:30 2009
@@ -1,4 +1,4 @@
-// Copyright 2008 The Apache Software Foundation
+// Copyright 2008, 2009 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.
@@ -77,4 +77,25 @@
* @see org.apache.tapestry5.Binding#getBindingType()
*/
Class getBoundType();
+
+ /**
+ * Registers the given ParameterChangeListener. Registering a listener which is already
+ * registered is silently ignored.
+ * @param listener
+ * @since 5.2.0.0
+ */
+ void registerParameterChangeListener(ParameterChangeListener listener);
+
+ /**
+ * Unregisters the given listener. Unregistering a listener that isn't registered is not an error.
+ * @since 5.2.0.0
+ */
+ void unregisterParameterChangeListener(ParameterChangeListener listener);
+
+ /**
+ * @return true if the parameter value should be cached within a component during rendering
+ * @since 5.2.0.0
+ */
+ boolean shouldCache();
+
}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterChangeListener.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterChangeListener.java?rev=795264&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterChangeListener.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterChangeListener.java Fri Jul 17 22:53:30 2009
@@ -0,0 +1,33 @@
+// Copyright 2009 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.tapestry5.internal;
+
+/**
+ * An object that listens for changes to the underlying values in parameters.
+ * This is currently used to ensure that mixin fields annotated with
+ * {@link org.apache.tapestry5.annotations.BindParameter} and the underlying component variable will stay properly
+ * in sync, regardless of parameter caching.
+ * @since 5.2.0.0
+ */
+public interface ParameterChangeListener
+{
+ /**
+ * Called when the value of a component <code>@Parameter</code> field changes.
+ * Note that this event will be fired regardless of whether the parameter is bound.
+ *
+ * @param event
+ */
+ void parameterChanged(ParameterChangedEvent event);
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterChangedEvent.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterChangedEvent.java?rev=795264&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterChangedEvent.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/ParameterChangedEvent.java Fri Jul 17 22:53:30 2009
@@ -0,0 +1,49 @@
+// Copyright 2009 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.tapestry5.internal;
+
+/**
+ * Event associated with a parameter value changing, used by <code>ParameterChangeListener</code>s.
+ * @since 5.2.0.0
+ */
+public class ParameterChangedEvent<T>
+{
+ private final String parameterName;
+ private final T newValue;
+
+
+ public ParameterChangedEvent(String parameterName, T newValue)
+ {
+ this.parameterName = parameterName;
+ this.newValue = newValue;
+ }
+
+ /**
+ * @return the name of the parameter that changed.
+ */
+ public String getParameterName()
+ {
+ return parameterName;
+ }
+
+ /**
+ * @return the new value. May be null.
+ */
+ public T getNewValue()
+ {
+ return newValue;
+ }
+
+}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java Fri Jul 17 22:53:30 2009
@@ -17,6 +17,7 @@
import org.apache.tapestry5.*;
import org.apache.tapestry5.ioc.Messages;
import org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.ioc.Orderable;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.internal.util.Defense;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
@@ -474,4 +475,28 @@
}
};
}
+
+ /**
+ *
+ * @param mixinDef the original mixin definition.
+ * @return an Orderable whose id is the mixin name.
+ */
+ public static Orderable<String> mixinTypeAndOrder(String mixinDef) {
+ int idx = mixinDef.indexOf("::");
+ if (idx == -1)
+ {
+ return new Orderable(mixinDef,mixinDef);
+ }
+ String type = mixinDef.substring(0,idx);
+ String[] constraints = splitMixinConstraints(mixinDef.substring(idx+2));
+
+ return new Orderable(type,type,constraints);
+ }
+
+
+ public static String[] splitMixinConstraints(String s)
+ {
+ return InternalUtils.isBlank(s)?null:s.split(";");
+ }
+
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableComponentModelImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableComponentModelImpl.java?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableComponentModelImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableComponentModelImpl.java Fri Jul 17 22:53:30 2009
@@ -54,6 +54,8 @@
private List<String> mixinClassNames;
+ private Map<String, String[]> mixinOrders;
+
private boolean informalParametersSupported;
private boolean mixinAfter;
@@ -104,7 +106,7 @@
return componentClassName;
}
- public void addParameter(String name, boolean required, boolean allowNull, String defaultBindingPrefix)
+ public void addParameter(String name, boolean required, boolean allowNull, String defaultBindingPrefix, boolean cached)
{
Defense.notBlank(name, "name");
Defense.notBlank(defaultBindingPrefix, "defaultBindingPrefix");
@@ -117,7 +119,16 @@
if (parameters.containsKey(name))
throw new IllegalArgumentException(ModelMessages.duplicateParameter(name, componentClassName));
- parameters.put(name, new ParameterModelImpl(name, required, allowNull, defaultBindingPrefix));
+ parameters.put(name, new ParameterModelImpl(name, required, allowNull, defaultBindingPrefix,cached));
+ }
+
+ public void addParameter(String name, boolean required, boolean allowNull, String defaultBindingPrefix)
+ {
+ //assume /false/ for the default because:
+ //if the parameter is actually cached, the only effect will be to reduce that optimization in certain
+ //scenarios (mixin BindParameter). But if the value is NOT cached but we say it is,
+ //we'll get incorrect behavior.
+ addParameter(name,required,allowNull,defaultBindingPrefix,false);
}
public ParameterModel getParameterModel(String parameterName)
@@ -220,11 +231,16 @@
return parentModel == null;
}
- public void addMixinClassName(String mixinClassName)
+ public void addMixinClassName(String mixinClassName, String... order)
{
if (mixinClassNames == null) mixinClassNames = CollectionFactory.newList();
mixinClassNames.add(mixinClassName);
+ if (order != null && order.length > 0)
+ {
+ if (mixinOrders == null) mixinOrders = CollectionFactory.newCaseInsensitiveMap();
+ mixinOrders.put(mixinClassName,order);
+ }
}
public List<String> getMixinClassNames()
@@ -324,4 +340,9 @@
? false
: parentModel.handlesEvent(eventType);
}
+
+ public String[] getOrderForMixin(String mixinClassName)
+ {
+ return InternalUtils.get(mixinOrders,mixinClassName);
+ }
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableEmbeddedComponentModelImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableEmbeddedComponentModelImpl.java?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableEmbeddedComponentModelImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableEmbeddedComponentModelImpl.java Fri Jul 17 22:53:30 2009
@@ -46,6 +46,8 @@
*/
private List<String> mixinClassNames;
+ private Map<String,String[]> mixinConstraints;
+
public MutableEmbeddedComponentModelImpl(String id, String componentType, String componentClassName,
String declaredClass, boolean inheritInformalParameters, Location location)
{
@@ -106,11 +108,12 @@
return Collections.unmodifiableList(mixinClassNames);
}
- public void addMixin(String mixinClassName)
+ public void addMixin(String mixinClassName, String... constraints)
{
if (mixinClassNames == null)
{
mixinClassNames = CollectionFactory.newList();
+ mixinConstraints = CollectionFactory.newCaseInsensitiveMap();
}
else
{
@@ -119,6 +122,7 @@
}
mixinClassNames.add(mixinClassName);
+ mixinConstraints.put(mixinClassName,constraints);
}
public boolean getInheritInformalParameters()
@@ -137,4 +141,9 @@
{
return publishedParameters;
}
+
+ public String[] getConstraintsForMixin(String mixinClassName)
+ {
+ return InternalUtils.get(mixinConstraints,mixinClassName);
+ }
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/ParameterModelImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/ParameterModelImpl.java?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/ParameterModelImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/ParameterModelImpl.java Fri Jul 17 22:53:30 2009
@@ -1,4 +1,4 @@
-// Copyright 2006, 2008 The Apache Software Foundation
+// Copyright 2006, 2008, 2009 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.
@@ -26,12 +26,20 @@
private final String defaultBindingPrefix;
- public ParameterModelImpl(String name, boolean required, boolean allowNull, String defaultBindingPrefix)
+ private final boolean cached;
+
+ public ParameterModelImpl(
+ String name,
+ boolean required,
+ boolean allowNull,
+ String defaultBindingPrefix,
+ boolean cached)
{
this.name = name;
this.required = required;
this.allowNull = allowNull;
this.defaultBindingPrefix = defaultBindingPrefix;
+ this.cached = cached;
}
public String getName()
@@ -53,4 +61,9 @@
{
return allowNull;
}
+
+ public boolean isCached()
+ {
+ return cached;
+ }
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentAssemblerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentAssemblerImpl.java?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentAssemblerImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/ComponentAssemblerImpl.java Fri Jul 17 22:53:30 2009
@@ -107,10 +107,11 @@
}
catch (RuntimeException ex)
{
- throw new RuntimeException(String.format("Exception assembling root component of page %s: %s",
- pageAssembly.page.getName(),
- InternalUtils.toMessage(ex)),
- ex);
+ throw new RuntimeException(
+ PageloadMessages.exceptionAssemblingRootComponent(
+ pageAssembly.page.getName(),
+ InternalUtils.toMessage(ex)),
+ ex);
}
}
@@ -120,7 +121,9 @@
{
Instantiator mixinInstantiator = instantiatorSource.getInstantiator(className);
- element.addMixin(InternalUtils.lastTerm(className), mixinInstantiator);
+ ComponentModel model = instantiator.getModel();
+ element.addMixin(InternalUtils.lastTerm(className),
+ mixinInstantiator,model.getOrderForMixin(className));
}
}
@@ -159,13 +162,15 @@
}
catch (RuntimeException ex)
{
- String message = String.format("Exception assembling embedded component '%s' (of type %s, within %s): %s",
- embeddedId,
- instantiator.getModel().getComponentClassName(),
- container.getCompleteId(),
- InternalUtils.toMessage(ex));
-
- throw new TapestryException(message, location, ex);
+ throw new TapestryException(
+ PageloadMessages.exceptionAssemblingEmbeddedComponent(
+ embeddedId,
+ instantiator.getModel().getComponentClassName(),
+ container.getCompleteId(),
+ InternalUtils.toMessage(ex)
+ ),
+ location,
+ ex);
}
}
@@ -232,9 +237,7 @@
String className = getModel().getComponentClassName();
throw new RuntimeException(
- String.format(
- "Embedded component(s) %s are defined within component class %s (or a super-class of %s), " +
- "but are not present in the component template (%s).",
+ PageloadMessages.embeddedComponentsNotInTemplate(
InternalUtils.joinSorted(embeddedIds.keySet()),
className,
InternalUtils.lastTerm(className),
@@ -263,6 +266,11 @@
{
try
{
+
+ if (InternalUtils.isBlank(componentClassName))
+ {
+ throw new TapestryException(PageloadMessages.missingComponentType(),location,null);
+ }
EmbeddedComponentAssemblerImpl embedded = new EmbeddedComponentAssemblerImpl(assemblerSource,
instantiatorSource,
componentClassResolver,
@@ -288,14 +296,12 @@
if (existingEmbeddedId != null)
{
- String message = String.format(
- "Parameter '%s' of embedded component '%s' can not be published as a parameter of component %s, as it has previously been published by embedded component '%s'.",
- publishedParameterName,
- embeddedId,
- instantiator.getModel().getComponentClassName(),
- existingEmbeddedId);
-
- throw new TapestryException(message, location, null);
+ throw new TapestryException(
+ PageloadMessages.parameterAlreadyPublished(
+ publishedParameterName,
+ embeddedId,
+ instantiator.getModel().getComponentClassName(),
+ existingEmbeddedId) , location, null);
}
publishedParameterToEmbeddedId.put(publishedParameterName, embeddedId);
@@ -307,12 +313,14 @@
}
catch (Exception ex)
{
- String message = String.format("Failure creating embedded component '%s' of %s: %s",
- embeddedId,
- instantiator.getModel().getComponentClassName(),
- InternalUtils.toMessage(ex));
-
- throw new TapestryException(message, location, ex);
+ throw new TapestryException(
+ PageloadMessages.failureCreatingEmbeddedComponent(
+ embeddedId,
+ instantiator.getModel().getComponentClassName(),
+ InternalUtils.toMessage(ex)
+ ),
+ location,
+ ex);
}
}
@@ -354,9 +362,7 @@
if (innerBinder == null)
{
- String message = String.format(
- "Parameter '%s' of component %s is improperly published from embedded component '%s' (where it does not exist). " +
- "This may be a typo in the publishParameters attribute of the @Component annotation.",
+ String message = PageloadMessages.publishedParameterNonexistant(
parameterName,
instantiator.getModel().getComponentClassName(),
embeddedId);
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/CompositeRenderCommand.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/CompositeRenderCommand.java?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/CompositeRenderCommand.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/CompositeRenderCommand.java Fri Jul 17 22:53:30 2009
@@ -49,8 +49,7 @@
private void nyi(String methodName)
{
throw new IllegalStateException(
- String.format("RenderQueue method %s() is not implemented for composited render commands.",
- methodName));
+ PageloadMessages.compositeRenderCommandMethodNotImplemented(methodName));
}
};
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/EmbeddedComponentAssemblerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/EmbeddedComponentAssemblerImpl.java?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/EmbeddedComponentAssemblerImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/EmbeddedComponentAssemblerImpl.java Fri Jul 17 22:53:30 2009
@@ -15,10 +15,12 @@
package org.apache.tapestry5.internal.pageload;
import org.apache.tapestry5.internal.TapestryInternalUtils;
+
import org.apache.tapestry5.internal.services.ComponentInstantiatorSource;
import org.apache.tapestry5.internal.services.Instantiator;
import org.apache.tapestry5.internal.structure.ComponentPageElement;
import org.apache.tapestry5.ioc.Location;
+import org.apache.tapestry5.ioc.Orderable;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.ioc.internal.util.TapestryException;
@@ -42,6 +44,7 @@
private final Location location;
private final Map<String, Instantiator> mixinIdToInstantiator = CollectionFactory.newCaseInsensitiveMap();
+ private final Map<String, String[]> mixinsIdToOrderConstraints = CollectionFactory.newCaseInsensitiveMap();
/**
* Maps parameter names (both simple, and qualified with the mixin id) to the corresponding QualifiedParameterName.
@@ -85,7 +88,7 @@
for (String className : componentModel.getMixinClassNames())
{
- addMixin(className);
+ addMixin(className,componentModel.getOrderForMixin(className));
}
// If there's an embedded model (i.e., there was an @Component annotation)
@@ -95,17 +98,18 @@
{
for (String className : embeddedModel.getMixinClassNames())
{
- addMixin(className);
+ addMixin(className,embeddedModel.getConstraintsForMixin(className));
}
}
// And the template may include a t:mixins element to define yet more mixin.
-
- for (String mixinType : TapestryInternalUtils.splitAtCommas(templateMixins))
+ // Template strings specified as:
+ for (String mixinDef : TapestryInternalUtils.splitAtCommas(templateMixins))
{
- String className = componentClassResolver.resolveMixinTypeToClassName(mixinType);
+ Orderable<String> order = TapestryInternalUtils.mixinTypeAndOrder(mixinDef);
+ String className = componentClassResolver.resolveMixinTypeToClassName(order.getId());
- addMixin(className);
+ addMixin(className,order.getConstraints());
}
informalParametersMixinId = prescanMixins();
@@ -152,7 +156,7 @@
}
}
- private void addMixin(String className)
+ private void addMixin(String className, String... order)
{
Instantiator mixinInstantiator = instantiatorSource.getInstantiator(className);
@@ -160,12 +164,12 @@
if (mixinIdToInstantiator.containsKey(mixinId))
throw new TapestryException(
- String.format("Mixins applied to a component must be unique. Mixin '%s' has already been applied.",
- mixinId),
+ PageloadMessages.uniqueMixinRequired(mixinId),
location, null);
mixinIdToInstantiator.put(mixinId, mixinInstantiator);
+ mixinsIdToOrderConstraints.put(mixinId, order);
}
private ComponentModel getModel(String className)
@@ -186,11 +190,10 @@
String mixinId = parameterName.substring(0, dotx);
if (!mixinIdToInstantiator.containsKey(mixinId))
{
- String message = String.format("Mixin id for parameter '%s' not found. Attached mixins: %s.",
- parameterName,
- InternalUtils.joinSorted(mixinIdToInstantiator.keySet()));
-
- throw new TapestryException(message, location, null);
+ throw new TapestryException(
+ PageloadMessages.mixinidForParamnotfound(parameterName, mixinIdToInstantiator.keySet()),
+ location,
+ null);
}
}
else
@@ -244,7 +247,7 @@
String mixinId = entry.getKey();
Instantiator instantiator = entry.getValue();
- newElement.addMixin(mixinId, instantiator);
+ newElement.addMixin(mixinId, instantiator, mixinsIdToOrderConstraints.get(mixinId));
}
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageAssembly.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageAssembly.java?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageAssembly.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageAssembly.java Fri Jul 17 22:53:30 2009
@@ -82,11 +82,10 @@
if (e.getComponentResources().getComponentModel().getComponentClassName().equals(componentClassName))
{
- String message = String.format(
- "The template for component %s is recursive (contains another direct or indirect reference to component %<s). This is not supported (components may not contain themselves).",
- componentClassName);
-
- throw new TapestryException(message, location, null);
+ throw new TapestryException(
+ PageloadMessages.recursiveTemplate(componentClassName),
+ location,
+ null);
}
}
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageLoaderImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageLoaderImpl.java?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageLoaderImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageLoaderImpl.java Fri Jul 17 22:53:30 2009
@@ -305,13 +305,13 @@
Stack<TemplateToken> queue,
List<ComponentTemplate> overrideSearch)
{
- String extentionPointId = extensionPointToken.getExtentionPointId();
+ String extensionPointId = extensionPointToken.getExtensionPointId();
// Work up from the component, through its base classes, towards the last non-extension template.
for (ComponentTemplate t : overrideSearch)
{
- List<TemplateToken> tokens = t.getExtensionPointTokens(extentionPointId);
+ List<TemplateToken> tokens = t.getExtensionPointTokens(extensionPointId);
if (tokens != null)
{
@@ -324,7 +324,7 @@
// not find an override, somewhere, for it.
throw new TapestryException(
- String.format("Could not find an override for extension point '%s'.", extentionPointId),
+ PageloadMessages.couldNotFindOverride(extensionPointId),
extensionPointToken.getLocation(), null);
}
@@ -343,9 +343,7 @@
if (parentModel == null)
{
- throw new RuntimeException(String.format(
- "Component %s uses an extension template, but does not have a parent component.",
- model.getComponentClassName()));
+ throw new RuntimeException(PageloadMessages.noParentForExtension(model));
}
ComponentTemplate parentTemplate = templateSource.getTemplate(parentModel, assembler.getLocale());
@@ -433,7 +431,7 @@
break;
default:
- throw new IllegalStateException("Not yet implemented: " + context.peekType());
+ throw new IllegalStateException(PageloadMessages.tokenNotImplemented(context.peekType()));
}
}
@@ -528,12 +526,10 @@
if (binder == null)
{
- String message = String.format(
- "Component %s does not include a formal parameter '%s' (and does not support informal parameters).",
- element.getCompleteId(),
- parameterName);
-
- throw new TapestryException(message, token.getLocation(), null);
+ throw new TapestryException(
+ PageloadMessages.parameterNotSupported(element.getCompleteId(), parameterName),
+ token.getLocation(),
+ null);
}
binder.bind(pageAssembly.createdElement.peek(), binding);
@@ -706,15 +702,10 @@
if (InternalUtils.isNonBlank(modelType) && embeddedType != null)
{
- String message = String.format(
- "Embedded component '%s' provides a type attribute in the template ('%s') " +
- "as well as in the component class ('%s'). " +
- "You should not provide a type attribute in the template when defining an embedded component " +
- "within the component class.",
- embeddedId, embeddedType, modelType
- );
-
- throw new TapestryException(message, token, null);
+ throw new TapestryException(
+ PageloadMessages.redundantEmbeddedComponentTypes(embeddedId,embeddedType,modelType),
+ token,
+ null);
}
embeddedType = modelType;
@@ -882,7 +873,7 @@
* Adds a deferred action to the PageAssembly, to handle connecting the embedded components' parameter to the
* container component's parameter once everything else has been built.
*
- * @param assembler
+ * @param context
* @param parameterName
* @param containerParameterName
*/
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageloadMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageloadMessages.java?rev=795264&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageloadMessages.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageloadMessages.java Fri Jul 17 22:53:30 2009
@@ -0,0 +1,147 @@
+// Copyright 2009 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.tapestry5.internal.pageload;
+
+import org.apache.tapestry5.ioc.Messages;
+import org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.ioc.internal.util.MessagesImpl;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.internal.parser.TokenType;
+import org.apache.tapestry5.model.ComponentModel;
+
+import java.util.Collection;
+
+/**
+ * Localized exception message support for pageload-related classes.
+ * @since 5.2.0.0
+ */
+final class PageloadMessages
+{
+
+ private static final Messages MESSAGES = MessagesImpl.forClass(PageloadMessages.class);
+
+ private PageloadMessages()
+ {
+ }
+
+ public static String uniqueMixinRequired(String mixinId)
+ {
+ return MESSAGES.format("unique_mixin_required", mixinId);
+ }
+
+ public static String mixinidForParamnotfound(String parameterName, Collection<String> availableMixins)
+ {
+ return MESSAGES.format("mixinid_for_paramnotfound", parameterName, InternalUtils.joinSorted(availableMixins));
+ }
+
+ public static String missingComponentType()
+ {
+ return MESSAGES.get("missing_component_type");
+ }
+
+ public static String noMoreTokens()
+ {
+ return MESSAGES.get("no_more_tokens");
+ }
+
+ public static String couldNotFindOverride(String extensionPointId)
+ {
+ return MESSAGES.format("could_not_find_override", extensionPointId);
+ }
+
+ public static String noParentForExtension(ComponentModel model)
+ {
+ return MESSAGES.format("no_parent_for_extension", model.getComponentClassName());
+ }
+
+ public static String tokenNotImplemented(TokenType type)
+ {
+ return MESSAGES.format("token_not_implemented", type.toString());
+ }
+
+ public static String parameterNotSupported(String completeId, String parameterName)
+ {
+ return MESSAGES.format("parameter_not_supported", completeId, parameterName);
+ }
+
+ public static String redundantEmbeddedComponentTypes(String embeddedId, String embeddedType, String modelType)
+ {
+ return MESSAGES.format("redundant_embedded_component_types", embeddedId, embeddedType, modelType);
+ }
+
+ public static String recursiveTemplate(String componentClassName)
+ {
+ return MESSAGES.format("recursive_template", componentClassName);
+ }
+
+ public static String compositeRenderCommandMethodNotImplemented(String methodName)
+ {
+ return MESSAGES.format("composite_render_command_method_not_implemented",methodName);
+ }
+
+ public static String exceptionAssemblingRootComponent(String pageName, String exceptionMessage)
+ {
+ return MESSAGES.format("exception_assembling_root_component",pageName,exceptionMessage);
+ }
+
+ public static String exceptionAssemblingEmbeddedComponent(
+ String embeddedId,
+ String embeddedClassName,
+ String containerId,
+ String exception)
+ {
+ return MESSAGES.format("exception_assembling_embedded_component",
+ embeddedId,
+ embeddedClassName,
+ containerId,
+ exception);
+ }
+
+ public static String embeddedComponentsNotInTemplate(
+ String joinedComponentIds,
+ String qualifiedClassName,
+ String simpleClassName,
+ Resource templateResource)
+ {
+ return MESSAGES.format("embedded_components_not_in_template",
+ joinedComponentIds,
+ qualifiedClassName,
+ simpleClassName,
+ templateResource);
+ }
+
+ public static String parameterAlreadyPublished(
+ String publishedParameterName,
+ String embeddedId,
+ String componentClassName,
+ String existingEmbeddedId)
+ {
+ return MESSAGES.format("parameter_already_published",
+ publishedParameterName,
+ embeddedId,
+ componentClassName,
+ existingEmbeddedId);
+ }
+
+ public static String failureCreatingEmbeddedComponent(String embeddedId, String containerClass, String exception)
+ {
+ return MESSAGES.format("failure_creating_embedded_component",embeddedId, containerClass, exception);
+ }
+
+ public static String publishedParameterNonexistant(String parameterName, String publishingClass, String embeddedId)
+ {
+ return MESSAGES.format("published_parameter_nonexistant", parameterName, publishingClass, embeddedId);
+ }
+}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/TokenStreamImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/TokenStreamImpl.java?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/TokenStreamImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/TokenStreamImpl.java Fri Jul 17 22:53:30 2009
@@ -45,7 +45,7 @@
private void checkMore()
{
if (!more())
- throw new IllegalStateException("No more template tokens.");
+ throw new IllegalStateException(PageloadMessages.noMoreTokens());
}
public <T extends TemplateToken> T next(Class<T> type)
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/parser/ExtensionPointToken.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/parser/ExtensionPointToken.java?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/parser/ExtensionPointToken.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/parser/ExtensionPointToken.java Fri Jul 17 22:53:30 2009
@@ -25,16 +25,16 @@
*/
public class ExtensionPointToken extends TemplateToken
{
- private final String extentionPointId;
+ private final String extensionPointId;
- public ExtensionPointToken(String extentionPointId, Location location)
+ public ExtensionPointToken(String extensionPointId, Location location)
{
super(TokenType.EXTENSION_POINT, location);
- this.extentionPointId = extentionPointId;
+ this.extensionPointId = extensionPointId;
}
- public String getExtentionPointId()
+ public String getExtensionPointId()
{
- return extentionPointId;
+ return extensionPointId;
}
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/StaxTemplateParser.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/StaxTemplateParser.java?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/StaxTemplateParser.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/StaxTemplateParser.java Fri Jul 17 22:53:30 2009
@@ -303,10 +303,7 @@
{
int lineNumber = reader.getLocation().getLineNumber();
- if (cachedLocation != null && cachedLocation.getLine() != lineNumber)
- cachedLocation = null;
-
- if (cachedLocation == null)
+ if (cachedLocation == null || cachedLocation.getLine() != lineNumber)
cachedLocation = new LocationImpl(resource, lineNumber);
return cachedLocation;
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElement.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElement.java?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElement.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElement.java Fri Jul 17 22:53:30 2009
@@ -72,8 +72,9 @@
*
* @param mixinId a unique id for the mixin, the last term of the mixin's class name
* @param instantiator used to instantiate an instance of the mixin
+ * @param order Ordering strings used to determine the order of mixin execution.
*/
- void addMixin(String mixinId, Instantiator instantiator);
+ void addMixin(String mixinId, Instantiator instantiator, String... order);
/**
* @param mixinId id of previously added mixin
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java Fri Jul 17 22:53:30 2009
@@ -24,11 +24,8 @@
import org.apache.tapestry5.internal.util.NotificationEventCallback;
import org.apache.tapestry5.ioc.BaseLocatable;
import org.apache.tapestry5.ioc.Location;
-import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
-import org.apache.tapestry5.ioc.internal.util.Defense;
import static org.apache.tapestry5.ioc.internal.util.Defense.notBlank;
-import org.apache.tapestry5.ioc.internal.util.InternalUtils;
-import org.apache.tapestry5.ioc.internal.util.TapestryException;
+import org.apache.tapestry5.ioc.internal.util.*;
import org.apache.tapestry5.model.ComponentModel;
import org.apache.tapestry5.model.ParameterModel;
import org.apache.tapestry5.runtime.Component;
@@ -474,6 +471,12 @@
private final String id;
+ private Orderer<Component> mixinBeforeOrderer;
+
+ private Orderer<Component> mixinAfterOrderer;
+
+ private List<Runnable> deferredLoadActions;
+
private boolean loaded;
/**
@@ -667,7 +670,7 @@
children.put(childId, child);
}
- public void addMixin(String mixinId, Instantiator instantiator)
+ public void addMixin(String mixinId, Instantiator instantiator, String... order)
{
if (mixinIdToComponentResources == null)
{
@@ -684,8 +687,19 @@
instantiator);
mixinIdToComponentResources.put(mixinId, resources);
-
- components.add(resources.getComponent());
+ //note that since we're using explicit ordering now,
+ //we don't add anything to components until we page load; instead, we add
+ //to the orderers.
+ if (order == null) order = new String[0];
+ if (resources.getComponentModel().isMixinAfter())
+ {
+ if (mixinAfterOrderer == null) mixinAfterOrderer = new Orderer<Component>(getLogger());
+ mixinAfterOrderer.add(mixinId,resources.getComponent(),order);
+ } else
+ {
+ if (mixinBeforeOrderer == null) mixinBeforeOrderer =new Orderer<Component>(getLogger());
+ mixinBeforeOrderer.add(mixinId,resources.getComponent(),order);
+ }
}
public void bindMixinParameter(String mixinId, String parameterName, Binding binding)
@@ -753,37 +767,26 @@
public void containingPageDidLoad()
{
- // If this component has mixins, add the core component to the end of the list, after the
+ // If this component has mixins, order them according to:
// mixins.
if (components != null)
{
List<Component> ordered = CollectionFactory.newList();
- Iterator<Component> i = components.iterator();
-
- // Add all the normal components to the final list.
-
- while (i.hasNext())
- {
- Component mixin = i.next();
-
- if (mixin.getComponentResources().getComponentModel().isMixinAfter()) continue;
-
- ordered.add(mixin);
-
- // Remove from list, leaving just the late executing mixins
-
- i.remove();
- }
+ if (mixinBeforeOrderer != null)
+ ordered.addAll(mixinBeforeOrderer.getOrdered());
ordered.add(coreComponent);
// Add the remaining, late executing mixins
-
- ordered.addAll(components);
+ if (mixinAfterOrderer != null)
+ ordered.addAll(mixinAfterOrderer.getOrdered());
components = ordered;
+ //no need to keep the orderers around.
+ mixinBeforeOrderer = null;
+ mixinAfterOrderer = null;
}
loaded = true;
@@ -792,10 +795,19 @@
// that is invoked first, before we check for unbound parameters.
invoke(false, CONTAINING_PAGE_DID_LOAD);
-
+ executeDeferredLoadActions();
verifyRequiredParametersAreBound();
}
+ private void executeDeferredLoadActions()
+ {
+ if (deferredLoadActions == null) return;
+ for(Runnable action : deferredLoadActions)
+ {
+ action.run();
+ }
+ deferredLoadActions = null;//having executed them, we have no need now to store them.
+ }
public void enqueueBeforeRenderBody(RenderQueue queue)
{
@@ -857,24 +869,38 @@
public Component getMixinByClassName(String mixinClassName)
{
- Component result = null;
+ Component result = mixinForClassName(mixinClassName);
- if (mixinIdToComponentResources != null)
+
+ if (result == null) throw new TapestryException(StructureMessages.unknownMixin(completeId, mixinClassName),
+ getLocation(), null);
+
+ return result;
+ }
+
+ private Component mixinForClassName(String mixinClassName)
+ {
+
+ if (mixinIdToComponentResources == null) return null;
+ for (InternalComponentResources resources : mixinIdToComponentResources.values())
{
- for (InternalComponentResources resources : mixinIdToComponentResources.values())
+ if (resources.getComponentModel().getComponentClassName().equals(mixinClassName))
{
- if (resources.getComponentModel().getComponentClassName().equals(mixinClassName))
- {
- result = resources.getComponent();
- break;
- }
+ return resources.getComponent();
}
}
+ return null;
+ }
- if (result == null) throw new TapestryException(StructureMessages.unknownMixin(completeId, mixinClassName),
- getLocation(), null);
+ public boolean isMixingIn(String mixinClassName)
+ {
+ return mixinForClassName(mixinClassName) != null;
+ }
- return result;
+ public void deferLoadAction(Runnable action)
+ {
+ if (deferredLoadActions == null) deferredLoadActions = CollectionFactory.newList();
+ deferredLoadActions.add(action);
}
public ComponentResources getMixinResources(String mixinId)
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java Fri Jul 17 22:53:30 2009
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009 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.
@@ -15,8 +15,7 @@
package org.apache.tapestry5.internal.structure;
import org.apache.tapestry5.*;
-import org.apache.tapestry5.internal.InternalComponentResources;
-import org.apache.tapestry5.internal.ParameterAccess;
+import org.apache.tapestry5.internal.*;
import org.apache.tapestry5.internal.services.Instantiator;
import org.apache.tapestry5.ioc.AnnotationProvider;
import org.apache.tapestry5.ioc.Location;
@@ -37,6 +36,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
/**
* The bridge between a component and its {@link ComponentPageElement}, that supplies all kinds of resources to the
@@ -66,6 +66,10 @@
// Case insensitive map from parameter name to ParameterAccess
private Map<String, ParameterAccess> access;
+ // Case-insensitive map from container-parameter name to ParameterAccess, for BindParameter.
+ // Should only ever be used for mixins.
+ private Map<String, ParameterAccess> containerParameterAccess;
+
private Messages messages;
// Case insensitive
@@ -324,6 +328,16 @@
return element.getMixinByClassName(mixinClassName);
}
+ public boolean isMixingIn(String mixinClassName)
+ {
+ return element.isMixingIn(mixinClassName);
+ }
+
+ public void deferLoadAction(Runnable action)
+ {
+ element.deferLoadAction(action);
+ }
+
public void renderInformalParameters(MarkupWriter writer)
{
if (bindings == null) return;
@@ -474,6 +488,50 @@
return result;
}
+ public ParameterAccess getContainerBoundParameterAccess(final String boundParameterName, String... parentParameterNames)
+ {
+ if (containerParameterAccess == null) containerParameterAccess = CollectionFactory.newCaseInsensitiveMap();
+
+ ParameterAccess result = containerParameterAccess.get(boundParameterName);
+ if (result == null)
+ {
+ final InternalComponentResources res = (InternalComponentResources) getContainerResources();
+ //Ideally, this check would occur at class fabrication time. But there's not currently a way
+ //to tell if a component class is a mixin class, short of checking for "mixins" in the FQCN.
+ //So we check to make sure that this component class name is in the set of mixins defined for the container
+ //resources.
+ if (!res.isMixingIn(this.getComponentModel().getComponentClassName())) {
+ //then we're not a mixin, we're a component in the tree.
+ throw new TapestryException(StructureMessages.bindParameterOnlyOnMixin(boundParameterName, this),this,null);
+ }
+ //Have to be careful here. Problem is that if the mixin is not @MixinAfter, its PAGE_DID_LOAD will be called
+ //before the core component's. That can potentially result in missing default bindings if we
+ //call getParameterAcces at the wrong time (the unbound parameter access will be cached...).
+ String parentParameterName = findParentParameterName(parentParameterNames);
+ if (parentParameterName == null)
+ {
+ throw new TapestryException(
+ StructureMessages.noSuchCoreComponentParameter(this,boundParameterName,parentParameterNames),
+ this,null);
+ }
+ result = createContainerParameterAccess(parentParameterName);
+ containerParameterAccess.put(boundParameterName,result);
+ }
+ return result;
+ }
+
+ private String findParentParameterName(String... queries)
+ {
+ for(String query : queries)
+ {
+ if(getContainerResources().getComponentModel().getParameterModel(query) != null)
+ {
+ return query;
+ }
+ }
+ return null;
+ }
+
private ParameterAccess createParameterAccess(final String parameterName)
{
final Binding binding = getBinding(parameterName);
@@ -482,8 +540,11 @@
final boolean allowNull = parameterModel == null ? true : parameterModel.isAllowNull();
+ final boolean cache = parameterModel == null ? false : parameterModel.isCached();
+
return new ParameterAccess()
{
+
public boolean isBound()
{
return binding != null;
@@ -529,15 +590,24 @@
public <T> void write(T parameterValue)
{
- if (binding == null) return;
- Class bindingType = binding.getBindingType();
+ if (binding == null)
+ {
+ //have to fire in case there's a mixin watching value;
+ //even if it's not bound to any other value,
+ //the mixin needs to know that the value internal to the component
+ //was changed.
+ fireParameterChanged(parameterName, parameterValue);
+ return;
+ }
+ Class bindingType = binding.getBindingType();
try
{
Object coerced = elementResources.coerce(parameterValue, bindingType);
binding.set(coerced);
+ fireParameterChanged(parameterName,coerced);
}
catch (Exception ex)
{
@@ -561,6 +631,113 @@
{
return binding == null ? null : binding.getAnnotation(annotationClass);
}
+
+ private Set<ParameterChangeListener> listeners;
+
+ public void registerParameterChangeListener(ParameterChangeListener listener)
+ {
+ Defense.notNull(listener,"listener");
+ if (listeners == null) listeners = CollectionFactory.newSet();
+ listeners.add(listener);
+ }
+
+ public void unregisterParameterChangeListener(ParameterChangeListener listener)
+ {
+ if (listeners == null) return;
+ listeners.remove(listener);
+ }
+
+ public boolean shouldCache()
+ {
+ return cache;
+ }
+
+ protected void fireParameterChanged(String parameterName, Object newValue)
+ {
+ ParameterChangedEvent event = new ParameterChangedEvent(parameterName,newValue);
+ for(ParameterChangeListener l : listeners)
+ {
+ l.parameterChanged(event);
+ }
+ }
+
};
}
+
+ private ParameterAccess createContainerParameterAccess(final String parentParameterName)
+ {
+
+ return new ParameterAccess()
+ {
+
+ private ParameterAccess access()
+ {
+ return element.getComponentResources().getParameterAccess(parentParameterName);
+ }
+
+ public boolean isBound()
+ {
+ return element.getBinding(parentParameterName) != null;
+ }
+
+ public Object read(String desiredTypeName)
+ {
+ return access().read(desiredTypeName);
+ }
+
+ public <T> T read(Class<T> expectedType)
+ {
+ return access().read(expectedType);
+ }
+
+ public <T> void write(T parameterValue)
+ {
+ access().write(parameterValue);
+ }
+
+ public boolean isInvariant()
+ {
+ return access().isInvariant();
+ }
+
+ public Class getBoundType()
+ {
+ return access().getBoundType();
+ }
+
+ public void registerParameterChangeListener(final ParameterChangeListener listener)
+ {
+ //if it's not bound, try defering.
+ if (isBound())
+ {
+ access().registerParameterChangeListener(listener);
+ } else
+ {
+ //try waiting for it. If it's not bound after load, then it's not bound at all.
+ element.deferLoadAction(new Runnable() {
+ public void run()
+ {
+ access().registerParameterChangeListener(listener);
+ }
+ });
+ }
+ }
+
+ public void unregisterParameterChangeListener(ParameterChangeListener listener)
+ {
+ access().unregisterParameterChangeListener(listener);
+ }
+
+ public boolean shouldCache()
+ {
+ return access().shouldCache();
+ }
+
+ public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
+ {
+ return access().getAnnotation(annotationClass);
+ }
+ };
+
+ }
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StructureMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StructureMessages.java?rev=795264&r1=795263&r2=795264&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StructureMessages.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StructureMessages.java Fri Jul 17 22:53:30 2009
@@ -18,10 +18,12 @@
import org.apache.tapestry5.ioc.Messages;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.ioc.internal.util.MessagesImpl;
+import org.apache.tapestry5.ComponentResources;
import java.util.Collection;
import java.util.List;
import java.util.Set;
+import java.util.Arrays;
final class StructureMessages
{
@@ -119,4 +121,21 @@
{
return MESSAGES.get("persist-change-before-load-complete");
}
+
+ static String noSuchCoreComponentParameter(ComponentResources mixinResources,
+ String boundId, String...parameters)
+ {
+ return MESSAGES.format("no-such-core-component-parameter",
+ boundId,
+ mixinResources.getComponentModel().getComponentClassName(),
+ mixinResources.getContainerResources().getComponentModel().getComponentClassName(),
+ InternalUtils.joinSorted(Arrays.asList(parameters)),
+ InternalUtils.joinSorted(Arrays.asList(mixinResources.getContainerResources().getComponentModel().getDeclaredParameterNames())));
+ }
+
+ public static String bindParameterOnlyOnMixin(String boundParameterName, InternalComponentResourcesImpl internalComponentResources)
+ {
+ return MESSAGES.format("bind-parameter-only-on-mixin",boundParameterName,
+ internalComponentResources.getComponentModel().getComponentClassName());
+ }
}