You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by th...@apache.org on 2014/06/16 03:18:21 UTC
git commit: TAP5-1611 : out-of-the-box way in Tapestry for replacing
components
Repository: tapestry-5
Updated Branches:
refs/heads/master 9cd156f37 -> e6a83e031
TAP5-1611 : out-of-the-box way in Tapestry for replacing components
Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/e6a83e03
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/e6a83e03
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/e6a83e03
Branch: refs/heads/master
Commit: e6a83e031ad65eac99614f108392d06a8ff6c8f9
Parents: 9cd156f
Author: Thiago H. de Paula Figueiredo <th...@apache.org>
Authored: Sun Jun 15 22:17:47 2014 -0300
Committer: Thiago H. de Paula Figueiredo <th...@apache.org>
Committed: Sun Jun 15 22:17:47 2014 -0300
----------------------------------------------------------------------
.../internal/ComponentReplacerImpl.java | 89 ++++++++++++++++++++
.../tapestry5/modules/TapestryModule.java | 32 +++++++
.../tapestry5/services/ComponentReplacer.java | 51 +++++++++++
tapestry-core/src/test/app3/Login.tml | 6 ++
tapestry-core/src/test/app3/OverridePage.tml | 12 +++
.../src/test/app3/OverridePageAtComponent.tml | 12 +++
tapestry-core/src/test/app3/OverridenPage.tml | 8 ++
.../app3/AdditionalIntegrationTests.java | 21 +++++
.../app3/components/OverrideComponent.java | 24 ++++++
.../app3/components/OverridenComponent.java | 24 ++++++
.../integration/app3/mixins/OverrideMixin.java | 32 +++++++
.../integration/app3/mixins/OverridenMixin.java | 30 +++++++
.../integration/app3/pages/OverridePage.java | 18 ++++
.../app3/pages/OverridePageAtComponent.java | 35 ++++++++
.../integration/app3/pages/OverridenPage.java | 18 ++++
.../integration/app3/services/AppModule.java | 16 +++-
16 files changed, 427 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/main/java/org/apache/tapestry5/internal/ComponentReplacerImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/ComponentReplacerImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/ComponentReplacerImpl.java
new file mode 100644
index 0000000..8d7fc75
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/ComponentReplacerImpl.java
@@ -0,0 +1,89 @@
+// Copyright 2014 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;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.tapestry5.ioc.util.CaseInsensitiveMap;
+import org.apache.tapestry5.services.ComponentReplacer;
+import org.slf4j.Logger;
+
+public class ComponentReplacerImpl implements ComponentReplacer
+{
+
+ final Logger logger;
+ final private Map<Class, Class> replacements;
+ final private Map<String, Class> nameToClass;
+
+ @SuppressWarnings("rawtypes")
+ public ComponentReplacerImpl(Map<Class, Class> contributions, Logger logger)
+ {
+
+ this.logger = logger;
+ this.replacements = Collections.unmodifiableMap(contributions);
+ Map<String, Class> nameToClass = new HashMap<String, Class>();
+
+ int maxLength = 0;
+
+ for (Class<?> clasz : contributions.keySet())
+ {
+
+ final String name = clasz.getName();
+ if (name.length() > maxLength) {
+ maxLength = name.length();
+ }
+ nameToClass.put(name, contributions.get(clasz));
+
+ }
+
+ this.nameToClass = Collections.unmodifiableMap(nameToClass);
+
+ if (replacements.size() > 0 && logger.isInfoEnabled())
+ {
+
+ StringBuilder builder = new StringBuilder(1000);
+ final String format = "%" + maxLength + "s: %s\n";
+ builder.append("Component replacements (including components, pages and mixins):\n");
+ List<String> names = new ArrayList<String>(nameToClass.keySet());
+ Collections.sort(names);
+
+ for (String name : names) {
+ builder.append(String.format(format, name, nameToClass.get(name).getName()));
+ }
+
+ logger.info(builder.toString());
+
+ }
+
+ }
+
+ @Override
+ public Map<Class, Class> getReplacements()
+ {
+ return replacements;
+ }
+
+ @Override
+ public Class getReplacement(String className)
+ {
+ return nameToClass.get(className);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java b/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
index 63c114b..1a08540 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
@@ -65,7 +65,9 @@ import org.apache.tapestry5.ioc.util.AvailableValues;
import org.apache.tapestry5.ioc.util.StrategyRegistry;
import org.apache.tapestry5.json.JSONArray;
import org.apache.tapestry5.json.JSONObject;
+import org.apache.tapestry5.plastic.MethodAdvice;
import org.apache.tapestry5.plastic.MethodDescription;
+import org.apache.tapestry5.plastic.MethodInvocation;
import org.apache.tapestry5.runtime.Component;
import org.apache.tapestry5.runtime.ComponentResourcesAware;
import org.apache.tapestry5.runtime.RenderCommand;
@@ -95,6 +97,7 @@ import org.slf4j.Logger;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.math.BigDecimal;
@@ -372,6 +375,7 @@ public final class TapestryModule
binder.bind(DateUtilities.class, DateUtilitiesImpl.class);
binder.bind(PartialTemplateRenderer.class, PartialTemplateRendererImpl.class);
binder.bind(org.apache.tapestry5.services.exceptions.ExceptionReporter.class, ExceptionReporterImpl.class);
+ binder.bind(ComponentReplacer.class, ComponentReplacerImpl.class).eagerLoad();
}
// ========================================================================
@@ -2660,4 +2664,32 @@ public final class TapestryModule
{
return strategyBuilder.build(ValueLabelProvider.class, configuration);
}
+
+ @Advise(serviceInterface = ComponentInstantiatorSource.class)
+ public static void componentReplacer(MethodAdviceReceiver methodAdviceReceiver,
+ final ComponentReplacer componentReplacer) throws NoSuchMethodException, SecurityException {
+
+ if (componentReplacer.getReplacements().size() > 0) {
+
+ MethodAdvice advice = new MethodAdvice()
+ {
+ @Override
+ public void advise(MethodInvocation invocation)
+ {
+ String className = (String) invocation.getParameter(0);
+ final Class<?> replacement = componentReplacer.getReplacement(className);
+ if (replacement != null)
+ {
+ invocation.setParameter(0, replacement.getName());
+ }
+ invocation.proceed();
+ }
+ };
+
+ methodAdviceReceiver.adviseMethod(
+ ComponentInstantiatorSource.class.getMethod("getInstantiator", String.class), advice);
+
+ }
+ }
+
}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentReplacer.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentReplacer.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentReplacer.java
new file mode 100644
index 0000000..375a8c6
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentReplacer.java
@@ -0,0 +1,51 @@
+// Copyright 2014 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.services;
+
+import java.util.Map;
+
+import org.apache.tapestry5.ioc.MethodAdviceReceiver;
+import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration;
+
+/**
+ * Service that allows replacing one component, page or mixin class by another without changing the sources.
+ * This service shouldn't be used directly: it's not an internal service just because it receives
+ * contributions.
+ *
+ * Contributions to it are mapped: the key is the component, page or mixin class to be
+ * replaced, the value is the replacement.
+ *
+ * @since 5.4
+ * @see ComponentClassResolver.
+ */
+@UsesMappedConfiguration(key = Class.class, value = Class.class)
+public interface ComponentReplacer
+{
+
+ /**
+ * Returns an immutable map of replacements. Internal use only.
+ *
+ * @return a {@link Map}.
+ */
+ Map<Class, Class> getReplacements();
+
+ /**
+ * Returns the replacement for a class given its name.
+ * @param className the fully qualified class name.
+ * @return a {@link Class} or null.
+ */
+ Class getReplacement(String className);
+
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/app3/Login.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/app3/Login.tml b/tapestry-core/src/test/app3/Login.tml
index 9fba45c..6937cd2 100644
--- a/tapestry-core/src/test/app3/Login.tml
+++ b/tapestry-core/src/test/app3/Login.tml
@@ -22,6 +22,12 @@
<li>
<t:pagelink page="BeanEditorWithOverridenCssClassesDemo">BeanEditor with overriden CSS classes demo</t:pagelink>
</li>
+ <li>
+ <t:pagelink page="OverridenPage">ComponentReplacer demo</t:pagelink>
+ </li>
+ <li>
+ <t:pagelink page="OverridePageAtComponent">ComponentReplacer demo (using @Component to declare component instances)</t:pagelink>
+ </li>
</ul>
</body>
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/app3/OverridePage.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/app3/OverridePage.tml b/tapestry-core/src/test/app3/OverridePage.tml
new file mode 100644
index 0000000..cddb3e3
--- /dev/null
+++ b/tapestry-core/src/test/app3/OverridePage.tml
@@ -0,0 +1,12 @@
+<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+ <head>
+ <title>Override Page</title>
+ </head>
+ <body>
+ <h1>Override Page</h1>
+ <p>
+ Link with mixin <t:pagelink t:page="Index" t:mixins="OverridenMixin">Index</t:pagelink>
+ </p>
+ <t:overridenComponent/>
+ </body>
+</html>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/app3/OverridePageAtComponent.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/app3/OverridePageAtComponent.tml b/tapestry-core/src/test/app3/OverridePageAtComponent.tml
new file mode 100644
index 0000000..d10610b
--- /dev/null
+++ b/tapestry-core/src/test/app3/OverridePageAtComponent.tml
@@ -0,0 +1,12 @@
+<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+ <head>
+ <title>Override Page</title>
+ </head>
+ <body>
+ <h1>Override Page with @Component</h1>
+ <p>
+ Link with mixin <a t:id="link" href="#">Index</a>
+ </p>
+ <div t:id="overridenComponent"/>
+ </body>
+</html>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/app3/OverridenPage.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/app3/OverridenPage.tml b/tapestry-core/src/test/app3/OverridenPage.tml
new file mode 100644
index 0000000..cf9b1a7
--- /dev/null
+++ b/tapestry-core/src/test/app3/OverridenPage.tml
@@ -0,0 +1,8 @@
+<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+ <head>
+ <title>Overriden</title>
+ </head>
+ <body>
+ <h1>Overriden</h1>
+ </body>
+</html>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/AdditionalIntegrationTests.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/AdditionalIntegrationTests.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/AdditionalIntegrationTests.java
index 8e03e34..774df44 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/AdditionalIntegrationTests.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/AdditionalIntegrationTests.java
@@ -15,12 +15,14 @@
package org.apache.tapestry5.integration.app3;
import org.apache.tapestry5.integration.TapestryCoreTestCase;
+import org.apache.tapestry5.test.TapestryTestConfiguration;
import org.testng.annotations.Test;
/**
* Additional integration tests that do not fit with the main group due to the need for special
* configuration.
*/
+@TapestryTestConfiguration(webAppFolder = "src/test/app3")
public class AdditionalIntegrationTests extends TapestryCoreTestCase
{
/**
@@ -88,4 +90,23 @@ public class AdditionalIntegrationTests extends TapestryCoreTestCase
assertTextPresent("Communication with the server failed: Server-side exception.");
}
+
+ // TAP5-1611
+ @Test
+ public void component_replacer()
+ {
+
+ final String[] pageNames = {"ComponentReplacer demo", "ComponentReplacer demo (using @Component to declare component instances)"};
+ for (String pageName : pageNames)
+ {
+ openLinks(pageName);
+
+ assertTrue(isElementPresent("overrideMixin"));
+ assertFalse(isElementPresent("overridenMixin"));
+ assertTrue(isElementPresent("overrideComponent"));
+ assertFalse(isElementPresent("overridenComponent"));
+ }
+
+ }
+
}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/components/OverrideComponent.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/components/OverrideComponent.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/components/OverrideComponent.java
new file mode 100644
index 0000000..a43a745
--- /dev/null
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/components/OverrideComponent.java
@@ -0,0 +1,24 @@
+// Copyright 2014 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.integration.app3.components;
+
+import org.apache.tapestry5.MarkupWriter;
+
+public class OverrideComponent
+{
+ void beginRender(MarkupWriter writer) {
+ writer.element("div", "id", "overrideComponent").element("p").text("Override component");
+ writer.end(); // div
+ }
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/components/OverridenComponent.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/components/OverridenComponent.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/components/OverridenComponent.java
new file mode 100644
index 0000000..01f5130
--- /dev/null
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/components/OverridenComponent.java
@@ -0,0 +1,24 @@
+// Copyright 2014 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.integration.app3.components;
+
+import org.apache.tapestry5.MarkupWriter;
+
+public class OverridenComponent
+{
+ void beginRender(MarkupWriter writer) {
+ writer.element("div", "id", "overridenComponent").element("p").text("Overriden component");
+ writer.end(); // div
+ }
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/mixins/OverrideMixin.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/mixins/OverrideMixin.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/mixins/OverrideMixin.java
new file mode 100644
index 0000000..5614178
--- /dev/null
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/mixins/OverrideMixin.java
@@ -0,0 +1,32 @@
+// Copyright 2014 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.integration.app3.mixins;
+
+import org.apache.tapestry5.ClientElement;
+import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.annotations.InjectContainer;
+import org.apache.tapestry5.annotations.MixinAfter;
+import org.apache.tapestry5.dom.Element;
+
+@MixinAfter
+public class OverrideMixin
+{
+ @InjectContainer
+ private ClientElement clientElement;
+
+ void afterRender(MarkupWriter writer) {
+ final Element element = writer.getDocument().getElementById(clientElement.getClientId());
+ element.element("span", "id", "overrideMixin").text(" [Override mixin]");
+ }
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/mixins/OverridenMixin.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/mixins/OverridenMixin.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/mixins/OverridenMixin.java
new file mode 100644
index 0000000..50ea142
--- /dev/null
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/mixins/OverridenMixin.java
@@ -0,0 +1,30 @@
+// Copyright 2014 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.integration.app3.mixins;
+
+import org.apache.tapestry5.ClientElement;
+import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.annotations.InjectContainer;
+import org.apache.tapestry5.dom.Element;
+
+public class OverridenMixin
+{
+ @InjectContainer
+ private ClientElement clientElement;
+
+ void afterRender(MarkupWriter writer) {
+ final Element element = writer.getDocument().getElementById(clientElement.getClientId());
+ element.element("span", "id", "overridenMixin").text(" [Overriden mixin]");
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridePage.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridePage.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridePage.java
new file mode 100644
index 0000000..7f803b7
--- /dev/null
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridePage.java
@@ -0,0 +1,18 @@
+// Copyright 2014 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.integration.app3.pages;
+
+public class OverridePage
+{
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridePageAtComponent.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridePageAtComponent.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridePageAtComponent.java
new file mode 100644
index 0000000..33ba903
--- /dev/null
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridePageAtComponent.java
@@ -0,0 +1,35 @@
+// Copyright 2014 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.integration.app3.pages;
+
+import org.apache.tapestry5.annotations.Component;
+import org.apache.tapestry5.annotations.MixinClasses;
+import org.apache.tapestry5.corelib.components.PageLink;
+import org.apache.tapestry5.integration.app3.components.OverridenComponent;
+import org.apache.tapestry5.integration.app3.mixins.OverridenMixin;
+
+/**
+ * Same as OverridePage, but using @Component to declare components.
+ */
+public class OverridePageAtComponent
+{
+
+ @Component(id = "link", parameters={"page=Index"})
+ @MixinClasses(OverridenMixin.class)
+ private PageLink pageLink;
+
+ @Component
+ private OverridenComponent overridenComponent;
+
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridenPage.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridenPage.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridenPage.java
new file mode 100644
index 0000000..6a03091
--- /dev/null
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridenPage.java
@@ -0,0 +1,18 @@
+// Copyright 2014 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.integration.app3.pages;
+
+public class OverridenPage
+{
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/services/AppModule.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/services/AppModule.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/services/AppModule.java
index 2f4a73d..58648bd 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/services/AppModule.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/services/AppModule.java
@@ -15,10 +15,17 @@
package org.apache.tapestry5.integration.app3.services;
import org.apache.tapestry5.SymbolConstants;
+import org.apache.tapestry5.integration.app3.components.OverrideComponent;
+import org.apache.tapestry5.integration.app3.components.OverridenComponent;
+import org.apache.tapestry5.integration.app3.mixins.OverrideMixin;
+import org.apache.tapestry5.integration.app3.mixins.OverridenMixin;
+import org.apache.tapestry5.integration.app3.pages.OverridePage;
+import org.apache.tapestry5.integration.app3.pages.OverridenPage;
import org.apache.tapestry5.ioc.Configuration;
import org.apache.tapestry5.ioc.MappedConfiguration;
import org.apache.tapestry5.ioc.OrderedConfiguration;
import org.apache.tapestry5.ioc.annotations.Contribute;
+import org.apache.tapestry5.services.ComponentReplacer;
import org.apache.tapestry5.services.DisplayBlockContribution;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.compatibility.Compatibility;
@@ -58,7 +65,7 @@ public class AppModule
configuration.add(SymbolConstants.FORM_FIELD_CSS_CLASS, FORM_FIELD_CSS_CLASS_VALUE);
}
-
+
@Contribute(Compatibility.class)
public static void disableBackwardsCompatibleFeatures(MappedConfiguration<Trait, Boolean> configuration)
{
@@ -78,5 +85,12 @@ public class AppModule
}
}, "before:*");
}
+
+ @Contribute(ComponentReplacer.class)
+ public static void overridePageAndComponentAndMixin(MappedConfiguration<Class, Class> configuration) {
+ configuration.add(OverridenPage.class, OverridePage.class);
+ configuration.add(OverridenComponent.class, OverrideComponent.class);
+ configuration.add(OverridenMixin.class, OverrideMixin.class);
+ }
}
Re: git commit: TAP5-1611 : out-of-the-box way in Tapestry for
replacing components
Posted by "Nourredine K." <no...@gmail.com>.
I thought about a scenario where a component library overrides a tapestry
component and that you would replace it in its turn with your own
implementation. Also the case where 2 different component libraries
override the same component/mixin or page (you would choose which one to
keep - maybe a warning should be logged in this case).
But the .override() method should address both cases. So everything is ok
for me : )
Thank you!
Re: git commit: TAP5-1611 : out-of-the-box way in Tapestry for
replacing components
Posted by Thiago H de Paula Figueiredo <th...@gmail.com>.
On Mon, 16 Jun 2014 05:17:43 -0300, Nourredine K. <no...@gmail.com>
wrote:
> Hi Thiago!
Hi!
> Nice feature! Simple but very useful. Thanks you.
:)
> Just a thought. What about multiple contributions to the
> ComponentReplacer
> for the same page/component or mixin ?
They would work in the same way every MappedConfiguration would: use
configuration.override() instead of configuration.add().
> In a next improvement, it could be very handy to allow some kind of
> ordered configuration.
I thought about it, but then I couldn't find a good scenario in which
there would be more than a replacement for the same page, component or
mixin. If you think of some, please let me know. I imagine this new
feature/service to not be used often. Making it an ordered configuration
would make contributions to it harder with almost no.
Changing it from MappedConfiguration to OrderedConfiguration would be a
backward-compatible change so, if you want to present some compelling
reason to do it, please do it quickly, so we can implement the change
before 5.4 final is released.
--
Thiago H. de Paula Figueiredo
Tapestry, Java and Hibernate consultant and developer
http://machina.com.br
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
For additional commands, e-mail: dev-help@tapestry.apache.org
Re: git commit: TAP5-1611 : out-of-the-box way in Tapestry for
replacing components
Posted by "Nourredine K." <no...@gmail.com>.
Hi Thiago!
Nice feature! Simple but very useful. Thanks you.
Just a thought. What about multiple contributions to the ComponentReplacer
for the same page/component or mixin ?
In a next improvement, it could be very handy to allow some kind of ordered
configuration.
Nourredine.
2014-06-16 3:18 GMT+02:00 <th...@apache.org>:
> Repository: tapestry-5
> Updated Branches:
> refs/heads/master 9cd156f37 -> e6a83e031
>
>
> TAP5-1611 : out-of-the-box way in Tapestry for replacing components
>
>
> Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
> Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/e6a83e03
> Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/e6a83e03
> Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/e6a83e03
>
> Branch: refs/heads/master
> Commit: e6a83e031ad65eac99614f108392d06a8ff6c8f9
> Parents: 9cd156f
> Author: Thiago H. de Paula Figueiredo <th...@apache.org>
> Authored: Sun Jun 15 22:17:47 2014 -0300
> Committer: Thiago H. de Paula Figueiredo <th...@apache.org>
> Committed: Sun Jun 15 22:17:47 2014 -0300
>
> ----------------------------------------------------------------------
> .../internal/ComponentReplacerImpl.java | 89 ++++++++++++++++++++
> .../tapestry5/modules/TapestryModule.java | 32 +++++++
> .../tapestry5/services/ComponentReplacer.java | 51 +++++++++++
> tapestry-core/src/test/app3/Login.tml | 6 ++
> tapestry-core/src/test/app3/OverridePage.tml | 12 +++
> .../src/test/app3/OverridePageAtComponent.tml | 12 +++
> tapestry-core/src/test/app3/OverridenPage.tml | 8 ++
> .../app3/AdditionalIntegrationTests.java | 21 +++++
> .../app3/components/OverrideComponent.java | 24 ++++++
> .../app3/components/OverridenComponent.java | 24 ++++++
> .../integration/app3/mixins/OverrideMixin.java | 32 +++++++
> .../integration/app3/mixins/OverridenMixin.java | 30 +++++++
> .../integration/app3/pages/OverridePage.java | 18 ++++
> .../app3/pages/OverridePageAtComponent.java | 35 ++++++++
> .../integration/app3/pages/OverridenPage.java | 18 ++++
> .../integration/app3/services/AppModule.java | 16 +++-
> 16 files changed, 427 insertions(+), 1 deletion(-)
> ----------------------------------------------------------------------
>
>
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/main/java/org/apache/tapestry5/internal/ComponentReplacerImpl.java
> ----------------------------------------------------------------------
> diff --git
> a/tapestry-core/src/main/java/org/apache/tapestry5/internal/ComponentReplacerImpl.java
> b/tapestry-core/src/main/java/org/apache/tapestry5/internal/ComponentReplacerImpl.java
> new file mode 100644
> index 0000000..8d7fc75
> --- /dev/null
> +++
> b/tapestry-core/src/main/java/org/apache/tapestry5/internal/ComponentReplacerImpl.java
> @@ -0,0 +1,89 @@
> +// Copyright 2014 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;
> +
> +import java.util.ArrayList;
> +import java.util.Collections;
> +import java.util.HashMap;
> +import java.util.List;
> +import java.util.Map;
> +import java.util.Map.Entry;
> +import java.util.Set;
> +
> +import org.apache.tapestry5.ioc.util.CaseInsensitiveMap;
> +import org.apache.tapestry5.services.ComponentReplacer;
> +import org.slf4j.Logger;
> +
> +public class ComponentReplacerImpl implements ComponentReplacer
> +{
> +
> + final Logger logger;
> + final private Map<Class, Class> replacements;
> + final private Map<String, Class> nameToClass;
> +
> + @SuppressWarnings("rawtypes")
> + public ComponentReplacerImpl(Map<Class, Class> contributions, Logger
> logger)
> + {
> +
> + this.logger = logger;
> + this.replacements = Collections.unmodifiableMap(contributions);
> + Map<String, Class> nameToClass = new HashMap<String, Class>();
> +
> + int maxLength = 0;
> +
> + for (Class<?> clasz : contributions.keySet())
> + {
> +
> + final String name = clasz.getName();
> + if (name.length() > maxLength) {
> + maxLength = name.length();
> + }
> + nameToClass.put(name, contributions.get(clasz));
> +
> + }
> +
> + this.nameToClass = Collections.unmodifiableMap(nameToClass);
> +
> + if (replacements.size() > 0 && logger.isInfoEnabled())
> + {
> +
> + StringBuilder builder = new StringBuilder(1000);
> + final String format = "%" + maxLength + "s: %s\n";
> + builder.append("Component replacements (including components,
> pages and mixins):\n");
> + List<String> names = new
> ArrayList<String>(nameToClass.keySet());
> + Collections.sort(names);
> +
> + for (String name : names) {
> + builder.append(String.format(format, name,
> nameToClass.get(name).getName()));
> + }
> +
> + logger.info(builder.toString());
> +
> + }
> +
> + }
> +
> + @Override
> + public Map<Class, Class> getReplacements()
> + {
> + return replacements;
> + }
> +
> + @Override
> + public Class getReplacement(String className)
> + {
> + return nameToClass.get(className);
> + }
> +
> +}
>
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
> ----------------------------------------------------------------------
> diff --git
> a/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
> b/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
> index 63c114b..1a08540 100644
> ---
> a/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
> +++
> b/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
> @@ -65,7 +65,9 @@ import org.apache.tapestry5.ioc.util.AvailableValues;
> import org.apache.tapestry5.ioc.util.StrategyRegistry;
> import org.apache.tapestry5.json.JSONArray;
> import org.apache.tapestry5.json.JSONObject;
> +import org.apache.tapestry5.plastic.MethodAdvice;
> import org.apache.tapestry5.plastic.MethodDescription;
> +import org.apache.tapestry5.plastic.MethodInvocation;
> import org.apache.tapestry5.runtime.Component;
> import org.apache.tapestry5.runtime.ComponentResourcesAware;
> import org.apache.tapestry5.runtime.RenderCommand;
> @@ -95,6 +97,7 @@ import org.slf4j.Logger;
> import javax.servlet.ServletContext;
> import javax.servlet.http.HttpServletRequest;
> import javax.servlet.http.HttpServletResponse;
> +
> import java.io.IOException;
> import java.lang.annotation.Annotation;
> import java.math.BigDecimal;
> @@ -372,6 +375,7 @@ public final class TapestryModule
> binder.bind(DateUtilities.class, DateUtilitiesImpl.class);
> binder.bind(PartialTemplateRenderer.class,
> PartialTemplateRendererImpl.class);
>
> binder.bind(org.apache.tapestry5.services.exceptions.ExceptionReporter.class,
> ExceptionReporterImpl.class);
> + binder.bind(ComponentReplacer.class,
> ComponentReplacerImpl.class).eagerLoad();
> }
>
> //
> ========================================================================
> @@ -2660,4 +2664,32 @@ public final class TapestryModule
> {
> return strategyBuilder.build(ValueLabelProvider.class,
> configuration);
> }
> +
> + @Advise(serviceInterface = ComponentInstantiatorSource.class)
> + public static void componentReplacer(MethodAdviceReceiver
> methodAdviceReceiver,
> + final ComponentReplacer componentReplacer) throws
> NoSuchMethodException, SecurityException {
> +
> + if (componentReplacer.getReplacements().size() > 0) {
> +
> + MethodAdvice advice = new MethodAdvice()
> + {
> + @Override
> + public void advise(MethodInvocation invocation)
> + {
> + String className = (String)
> invocation.getParameter(0);
> + final Class<?> replacement =
> componentReplacer.getReplacement(className);
> + if (replacement != null)
> + {
> + invocation.setParameter(0, replacement.getName());
> + }
> + invocation.proceed();
> + }
> + };
> +
> + methodAdviceReceiver.adviseMethod(
> +
> ComponentInstantiatorSource.class.getMethod("getInstantiator",
> String.class), advice);
> +
> + }
> + }
> +
> }
>
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentReplacer.java
> ----------------------------------------------------------------------
> diff --git
> a/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentReplacer.java
> b/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentReplacer.java
> new file mode 100644
> index 0000000..375a8c6
> --- /dev/null
> +++
> b/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentReplacer.java
> @@ -0,0 +1,51 @@
> +// Copyright 2014 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.services;
> +
> +import java.util.Map;
> +
> +import org.apache.tapestry5.ioc.MethodAdviceReceiver;
> +import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration;
> +
> +/**
> + * Service that allows replacing one component, page or mixin class by
> another without changing the sources.
> + * This service shouldn't be used directly: it's not an internal service
> just because it receives
> + * contributions.
> + *
> + * Contributions to it are mapped: the key is the component, page or
> mixin class to be
> + * replaced, the value is the replacement.
> + *
> + * @since 5.4
> + * @see ComponentClassResolver.
> + */
> +@UsesMappedConfiguration(key = Class.class, value = Class.class)
> +public interface ComponentReplacer
> +{
> +
> + /**
> + * Returns an immutable map of replacements. Internal use only.
> + *
> + * @return a {@link Map}.
> + */
> + Map<Class, Class> getReplacements();
> +
> + /**
> + * Returns the replacement for a class given its name.
> + * @param className the fully qualified class name.
> + * @return a {@link Class} or null.
> + */
> + Class getReplacement(String className);
> +
> +}
>
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/app3/Login.tml
> ----------------------------------------------------------------------
> diff --git a/tapestry-core/src/test/app3/Login.tml
> b/tapestry-core/src/test/app3/Login.tml
> index 9fba45c..6937cd2 100644
> --- a/tapestry-core/src/test/app3/Login.tml
> +++ b/tapestry-core/src/test/app3/Login.tml
> @@ -22,6 +22,12 @@
> <li>
> <t:pagelink
> page="BeanEditorWithOverridenCssClassesDemo">BeanEditor with overriden CSS
> classes demo</t:pagelink>
> </li>
> + <li>
> + <t:pagelink page="OverridenPage">ComponentReplacer
> demo</t:pagelink>
> + </li>
> + <li>
> + <t:pagelink
> page="OverridePageAtComponent">ComponentReplacer demo (using @Component to
> declare component instances)</t:pagelink>
> + </li>
> </ul>
>
> </body>
>
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/app3/OverridePage.tml
> ----------------------------------------------------------------------
> diff --git a/tapestry-core/src/test/app3/OverridePage.tml
> b/tapestry-core/src/test/app3/OverridePage.tml
> new file mode 100644
> index 0000000..cddb3e3
> --- /dev/null
> +++ b/tapestry-core/src/test/app3/OverridePage.tml
> @@ -0,0 +1,12 @@
> +<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
> + <head>
> + <title>Override Page</title>
> + </head>
> + <body>
> + <h1>Override Page</h1>
> + <p>
> + Link with mixin <t:pagelink t:page="Index"
> t:mixins="OverridenMixin">Index</t:pagelink>
> + </p>
> + <t:overridenComponent/>
> + </body>
> +</html>
> \ No newline at end of file
>
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/app3/OverridePageAtComponent.tml
> ----------------------------------------------------------------------
> diff --git a/tapestry-core/src/test/app3/OverridePageAtComponent.tml
> b/tapestry-core/src/test/app3/OverridePageAtComponent.tml
> new file mode 100644
> index 0000000..d10610b
> --- /dev/null
> +++ b/tapestry-core/src/test/app3/OverridePageAtComponent.tml
> @@ -0,0 +1,12 @@
> +<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
> + <head>
> + <title>Override Page</title>
> + </head>
> + <body>
> + <h1>Override Page with @Component</h1>
> + <p>
> + Link with mixin <a t:id="link" href="#">Index</a>
> + </p>
> + <div t:id="overridenComponent"/>
> + </body>
> +</html>
> \ No newline at end of file
>
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/app3/OverridenPage.tml
> ----------------------------------------------------------------------
> diff --git a/tapestry-core/src/test/app3/OverridenPage.tml
> b/tapestry-core/src/test/app3/OverridenPage.tml
> new file mode 100644
> index 0000000..cf9b1a7
> --- /dev/null
> +++ b/tapestry-core/src/test/app3/OverridenPage.tml
> @@ -0,0 +1,8 @@
> +<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
> + <head>
> + <title>Overriden</title>
> + </head>
> + <body>
> + <h1>Overriden</h1>
> + </body>
> +</html>
> \ No newline at end of file
>
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/AdditionalIntegrationTests.java
> ----------------------------------------------------------------------
> diff --git
> a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/AdditionalIntegrationTests.java
> b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/AdditionalIntegrationTests.java
> index 8e03e34..774df44 100644
> ---
> a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/AdditionalIntegrationTests.java
> +++
> b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/AdditionalIntegrationTests.java
> @@ -15,12 +15,14 @@
> package org.apache.tapestry5.integration.app3;
>
> import org.apache.tapestry5.integration.TapestryCoreTestCase;
> +import org.apache.tapestry5.test.TapestryTestConfiguration;
> import org.testng.annotations.Test;
>
> /**
> * Additional integration tests that do not fit with the main group due
> to the need for special
> * configuration.
> */
> +@TapestryTestConfiguration(webAppFolder = "src/test/app3")
> public class AdditionalIntegrationTests extends TapestryCoreTestCase
> {
> /**
> @@ -88,4 +90,23 @@ public class AdditionalIntegrationTests extends
> TapestryCoreTestCase
>
> assertTextPresent("Communication with the server failed:
> Server-side exception.");
> }
> +
> + // TAP5-1611
> + @Test
> + public void component_replacer()
> + {
> +
> + final String[] pageNames = {"ComponentReplacer demo",
> "ComponentReplacer demo (using @Component to declare component instances)"};
> + for (String pageName : pageNames)
> + {
> + openLinks(pageName);
> +
> + assertTrue(isElementPresent("overrideMixin"));
> + assertFalse(isElementPresent("overridenMixin"));
> + assertTrue(isElementPresent("overrideComponent"));
> + assertFalse(isElementPresent("overridenComponent"));
> + }
> +
> + }
> +
> }
>
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/components/OverrideComponent.java
> ----------------------------------------------------------------------
> diff --git
> a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/components/OverrideComponent.java
> b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/components/OverrideComponent.java
> new file mode 100644
> index 0000000..a43a745
> --- /dev/null
> +++
> b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/components/OverrideComponent.java
> @@ -0,0 +1,24 @@
> +// Copyright 2014 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.integration.app3.components;
> +
> +import org.apache.tapestry5.MarkupWriter;
> +
> +public class OverrideComponent
> +{
> + void beginRender(MarkupWriter writer) {
> + writer.element("div", "id",
> "overrideComponent").element("p").text("Override component");
> + writer.end(); // div
> + }
> +}
>
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/components/OverridenComponent.java
> ----------------------------------------------------------------------
> diff --git
> a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/components/OverridenComponent.java
> b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/components/OverridenComponent.java
> new file mode 100644
> index 0000000..01f5130
> --- /dev/null
> +++
> b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/components/OverridenComponent.java
> @@ -0,0 +1,24 @@
> +// Copyright 2014 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.integration.app3.components;
> +
> +import org.apache.tapestry5.MarkupWriter;
> +
> +public class OverridenComponent
> +{
> + void beginRender(MarkupWriter writer) {
> + writer.element("div", "id",
> "overridenComponent").element("p").text("Overriden component");
> + writer.end(); // div
> + }
> +}
>
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/mixins/OverrideMixin.java
> ----------------------------------------------------------------------
> diff --git
> a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/mixins/OverrideMixin.java
> b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/mixins/OverrideMixin.java
> new file mode 100644
> index 0000000..5614178
> --- /dev/null
> +++
> b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/mixins/OverrideMixin.java
> @@ -0,0 +1,32 @@
> +// Copyright 2014 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.integration.app3.mixins;
> +
> +import org.apache.tapestry5.ClientElement;
> +import org.apache.tapestry5.MarkupWriter;
> +import org.apache.tapestry5.annotations.InjectContainer;
> +import org.apache.tapestry5.annotations.MixinAfter;
> +import org.apache.tapestry5.dom.Element;
> +
> +@MixinAfter
> +public class OverrideMixin
> +{
> + @InjectContainer
> + private ClientElement clientElement;
> +
> + void afterRender(MarkupWriter writer) {
> + final Element element =
> writer.getDocument().getElementById(clientElement.getClientId());
> + element.element("span", "id", "overrideMixin").text(" [Override
> mixin]");
> + }
> +}
>
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/mixins/OverridenMixin.java
> ----------------------------------------------------------------------
> diff --git
> a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/mixins/OverridenMixin.java
> b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/mixins/OverridenMixin.java
> new file mode 100644
> index 0000000..50ea142
> --- /dev/null
> +++
> b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/mixins/OverridenMixin.java
> @@ -0,0 +1,30 @@
> +// Copyright 2014 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.integration.app3.mixins;
> +
> +import org.apache.tapestry5.ClientElement;
> +import org.apache.tapestry5.MarkupWriter;
> +import org.apache.tapestry5.annotations.InjectContainer;
> +import org.apache.tapestry5.dom.Element;
> +
> +public class OverridenMixin
> +{
> + @InjectContainer
> + private ClientElement clientElement;
> +
> + void afterRender(MarkupWriter writer) {
> + final Element element =
> writer.getDocument().getElementById(clientElement.getClientId());
> + element.element("span", "id", "overridenMixin").text(" [Overriden
> mixin]");
> + }
> +}
> \ No newline at end of file
>
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridePage.java
> ----------------------------------------------------------------------
> diff --git
> a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridePage.java
> b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridePage.java
> new file mode 100644
> index 0000000..7f803b7
> --- /dev/null
> +++
> b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridePage.java
> @@ -0,0 +1,18 @@
> +// Copyright 2014 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.integration.app3.pages;
> +
> +public class OverridePage
> +{
> +}
>
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridePageAtComponent.java
> ----------------------------------------------------------------------
> diff --git
> a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridePageAtComponent.java
> b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridePageAtComponent.java
> new file mode 100644
> index 0000000..33ba903
> --- /dev/null
> +++
> b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridePageAtComponent.java
> @@ -0,0 +1,35 @@
> +// Copyright 2014 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.integration.app3.pages;
> +
> +import org.apache.tapestry5.annotations.Component;
> +import org.apache.tapestry5.annotations.MixinClasses;
> +import org.apache.tapestry5.corelib.components.PageLink;
> +import
> org.apache.tapestry5.integration.app3.components.OverridenComponent;
> +import org.apache.tapestry5.integration.app3.mixins.OverridenMixin;
> +
> +/**
> + * Same as OverridePage, but using @Component to declare components.
> + */
> +public class OverridePageAtComponent
> +{
> +
> + @Component(id = "link", parameters={"page=Index"})
> + @MixinClasses(OverridenMixin.class)
> + private PageLink pageLink;
> +
> + @Component
> + private OverridenComponent overridenComponent;
> +
> +}
>
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridenPage.java
> ----------------------------------------------------------------------
> diff --git
> a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridenPage.java
> b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridenPage.java
> new file mode 100644
> index 0000000..6a03091
> --- /dev/null
> +++
> b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/pages/OverridenPage.java
> @@ -0,0 +1,18 @@
> +// Copyright 2014 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.integration.app3.pages;
> +
> +public class OverridenPage
> +{
> +}
>
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/e6a83e03/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/services/AppModule.java
> ----------------------------------------------------------------------
> diff --git
> a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/services/AppModule.java
> b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/services/AppModule.java
> index 2f4a73d..58648bd 100644
> ---
> a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/services/AppModule.java
> +++
> b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app3/services/AppModule.java
> @@ -15,10 +15,17 @@
> package org.apache.tapestry5.integration.app3.services;
>
> import org.apache.tapestry5.SymbolConstants;
> +import org.apache.tapestry5.integration.app3.components.OverrideComponent;
> +import
> org.apache.tapestry5.integration.app3.components.OverridenComponent;
> +import org.apache.tapestry5.integration.app3.mixins.OverrideMixin;
> +import org.apache.tapestry5.integration.app3.mixins.OverridenMixin;
> +import org.apache.tapestry5.integration.app3.pages.OverridePage;
> +import org.apache.tapestry5.integration.app3.pages.OverridenPage;
> import org.apache.tapestry5.ioc.Configuration;
> import org.apache.tapestry5.ioc.MappedConfiguration;
> import org.apache.tapestry5.ioc.OrderedConfiguration;
> import org.apache.tapestry5.ioc.annotations.Contribute;
> +import org.apache.tapestry5.services.ComponentReplacer;
> import org.apache.tapestry5.services.DisplayBlockContribution;
> import org.apache.tapestry5.services.Request;
> import org.apache.tapestry5.services.compatibility.Compatibility;
> @@ -58,7 +65,7 @@ public class AppModule
> configuration.add(SymbolConstants.FORM_FIELD_CSS_CLASS,
> FORM_FIELD_CSS_CLASS_VALUE);
>
> }
> -
> +
> @Contribute(Compatibility.class)
> public static void
> disableBackwardsCompatibleFeatures(MappedConfiguration<Trait, Boolean>
> configuration)
> {
> @@ -78,5 +85,12 @@ public class AppModule
> }
> }, "before:*");
> }
> +
> + @Contribute(ComponentReplacer.class)
> + public static void
> overridePageAndComponentAndMixin(MappedConfiguration<Class, Class>
> configuration) {
> + configuration.add(OverridenPage.class, OverridePage.class);
> + configuration.add(OverridenComponent.class,
> OverrideComponent.class);
> + configuration.add(OverridenMixin.class, OverrideMixin.class);
> + }
>
> }
>
>