You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2010/04/29 18:11:12 UTC

svn commit: r939358 - in /tapestry/tapestry5/trunk/tapestry-core/src: main/java/org/apache/tapestry5/internal/services/ main/java/org/apache/tapestry5/services/ test/java/org/apache/tapestry5/internal/services/

Author: hlship
Date: Thu Apr 29 16:11:11 2010
New Revision: 939358

URL: http://svn.apache.org/viewvc?rev=939358&view=rev
Log:
TAP5-1120: It is not possible to override the default Translator contributions to the TranslatorSource service

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TranslatorAlternatesSourceImpl.java   (with props)
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TranslatorAlternatesSource.java   (with props)
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/TranslatorAlternatesSourceImplTest.java   (with props)
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TranslatorSourceImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TranslatorSource.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/TranslatorSourceImplTest.java

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TranslatorAlternatesSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TranslatorAlternatesSourceImpl.java?rev=939358&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TranslatorAlternatesSourceImpl.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TranslatorAlternatesSourceImpl.java Thu Apr 29 16:11:11 2010
@@ -0,0 +1,45 @@
+// Copyright 2010 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.services;
+
+import java.util.Map;
+
+import org.apache.tapestry5.Translator;
+import org.apache.tapestry5.services.TranslatorAlternatesSource;
+
+@SuppressWarnings("unchecked")
+public class TranslatorAlternatesSourceImpl implements TranslatorAlternatesSource
+{
+    private final Map<String, Translator> configuration;
+
+    public TranslatorAlternatesSourceImpl(Map<String, Translator> configuration)
+    {
+        this.configuration = configuration;
+
+        for (Map.Entry<String, Translator> me : configuration.entrySet())
+        {
+            if (!me.getKey().equalsIgnoreCase(me.getValue().getName()))
+                throw new RuntimeException(String.format(
+                        "Contribution key '%s' does not match '%s' (the name of the corresponding Translator).", me
+                                .getKey(), me.getValue().getName()));
+        }
+    }
+
+    public Map<String, Translator> getTranslatorAlternates()
+    {
+        return configuration;
+    }
+
+}

Propchange: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TranslatorAlternatesSourceImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TranslatorSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TranslatorSourceImpl.java?rev=939358&r1=939357&r2=939358&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TranslatorSourceImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TranslatorSourceImpl.java Thu Apr 29 16:11:11 2010
@@ -14,6 +14,7 @@
 
 package org.apache.tapestry5.internal.services;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -25,14 +26,22 @@ import org.apache.tapestry5.ioc.util.Unk
 import org.apache.tapestry5.services.InvalidationListener;
 import org.apache.tapestry5.services.TranslatorSource;
 
+@SuppressWarnings("unchecked")
 public class TranslatorSourceImpl implements TranslatorSource, InvalidationListener
 {
-    private final Map<String, Translator> translators = CollectionFactory.newCaseInsensitiveMap();
+    private final Map<String, Translator> nameToTranslator = CollectionFactory.newCaseInsensitiveMap();
 
     private final StrategyRegistry<Translator> registry;
 
+    private static final Map<String, Translator> EMPTY = Collections.emptyMap();
+
     public TranslatorSourceImpl(Map<Class, Translator> configuration)
     {
+        this(configuration, EMPTY);
+    }
+
+    public TranslatorSourceImpl(Map<Class, Translator> configuration, Map<String, Translator> alternates)
+    {
         for (Map.Entry<Class, Translator> me : configuration.entrySet())
         {
             Class type = me.getKey();
@@ -45,7 +54,28 @@ public class TranslatorSourceImpl implem
                                         "Contributed translator for type %s reports its type as %s. Please change the contribution so that the key matches that translator type.",
                                         type.getName(), translator.getType().getName()));
 
-            translators.put(translator.getName(), translator);
+            String name = translator.getName();
+
+            if (nameToTranslator.containsKey(name))
+                throw new RuntimeException(
+                        String
+                                .format(
+                                        "Two different Translators contributed to the TranslatorSource service use the same translator name: '%s'.  Translator names must be unique.",
+                                        name));
+
+            nameToTranslator.put(name, translator);
+        }
+
+        for (String name : alternates.keySet())
+        {
+            if (nameToTranslator.containsKey(name))
+                throw new RuntimeException(
+                        String
+                                .format(
+                                        "Translator '%s' contributed to the TranslatorAlternatesSource service has the same name as a standard Translator contributed to the TranslatorSource service.",
+                                        name));
+
+            nameToTranslator.put(name, alternates.get(name));
         }
 
         registry = StrategyRegistry.newInstance(Translator.class, configuration, true);
@@ -53,12 +83,11 @@ public class TranslatorSourceImpl implem
 
     public Translator get(String name)
     {
-
-        Translator result = translators.get(name);
+        Translator result = nameToTranslator.get(name);
 
         if (result == null)
             throw new UnknownValueException(String.format("Unknown translator type '%s'.", name), new AvailableValues(
-                    "translators", translators));
+                    "translators", nameToTranslator));
 
         return result;
     }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java?rev=939358&r1=939357&r2=939358&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java Thu Apr 29 16:11:11 2010
@@ -371,6 +371,7 @@ public final class TapestryModule
         binder.bind(Dispatcher.class, AssetDispatcher.class).withId("AssetDispatcher");
         binder.bind(AssetPathConstructor.class, AssetPathConstructorImpl.class);
         binder.bind(JavascriptStackSource.class, JavascriptStackSourceImpl.class);
+        binder.bind(TranslatorAlternatesSource.class, TranslatorAlternatesSourceImpl.class);
     }
 
     // ========================================================================
@@ -1530,10 +1531,13 @@ public final class TapestryModule
         return service;
     }
 
-    public static TranslatorSource buildTranslatorSource(@Autobuild
-    TranslatorSourceImpl service, @ComponentClasses
-    InvalidationEventHub hub)
+    public static TranslatorSource buildTranslatorSource(Map<Class, Translator> configuration,
+            TranslatorAlternatesSource alternatesSource, @ComponentClasses
+            InvalidationEventHub hub)
     {
+        TranslatorSourceImpl service = new TranslatorSourceImpl(configuration, alternatesSource
+                .getTranslatorAlternates());
+
         hub.addInvalidationListener(service);
 
         return service;

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TranslatorAlternatesSource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TranslatorAlternatesSource.java?rev=939358&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TranslatorAlternatesSource.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TranslatorAlternatesSource.java Thu Apr 29 16:11:11 2010
@@ -0,0 +1,40 @@
+// Copyright 2010 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.Translator;
+import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration;
+
+/**
+ * This service is used by {@link TranslatorSource} to specify {@link Translator} <em>alternates</em>: translators that
+ * are used when specified explicitly by name. These translators may overlap the standard translators
+ * by type (thus requiring a separate configuration).
+ * <p>
+ * Translators contributed to this configuration must have names that do not overlap the standard translators. Further,
+ * the contribution key must match the {@linkplain Translator#getName() translator name}.
+ * 
+ * @since 5.2.0
+ */
+@UsesMappedConfiguration(Translator.class)
+public interface TranslatorAlternatesSource
+{
+    /**
+     * Get the mapping from name to Translator, based on the contributions to the service. It will be verified
+     * that the keys of the map corresponding to the names of the Translator values.
+     */
+    Map<String, Translator> getTranslatorAlternates();
+}

Propchange: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TranslatorAlternatesSource.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TranslatorSource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TranslatorSource.java?rev=939358&r1=939357&r2=939358&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TranslatorSource.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TranslatorSource.java Thu Apr 29 16:11:11 2010
@@ -4,7 +4,7 @@
 // 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
+// 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,
@@ -16,41 +16,57 @@ package org.apache.tapestry5.services;
 
 import org.apache.tapestry5.Translator;
 import org.apache.tapestry5.ioc.annotations.UsesConfiguration;
+import org.apache.tapestry5.ioc.util.StrategyRegistry;
 
 /**
- * A source for {@link org.apache.tapestry5.Translator}s, either by name or by property type.
+ * A source for {@link org.apache.tapestry5.Translator}s, either by name or by property type. The source knows
+ * about two sets of translators: the <em>standard</em> translators contributed directly to the service
+ * and the <em>alternate</em> translators, contributed to the {@link TranslatorAlternatesSource} service.
  * <p/>
- * The configuration includes all the translators; each contributed translator must have a unique {@linkplain
- * org.apache.tapestry5.Translator#getName() name}.
+ * Each contributed translator must have a unique {@linkplain org.apache.tapestry5.Translator#getName() name}.
+ * <p>
+ * Generally, Translators are matched by type (i.e., the type matching a particular property that will be read or
+ * updated). Contributions to this service use a {@link StrategyRegistry} to match by type. Translators can also be
+ * selected by name. The {@link TranslatorAlternatesSource} service configuration is often used for this purpose.
+ * <p>
+ * The contribution key must match the {@linkplain Translator#getType() translator type}.
  */
 @UsesConfiguration(Translator.class)
+@SuppressWarnings("unchecked")
 public interface TranslatorSource
 {
     /**
-     * Returns the translator with the given logical name.
-     *
-     * @param name name of translator (as configured)
+     * Returns the translator with the given name (either a standard translator, or an alternate).
+     * 
+     * @param name
+     *            name of translator (as configured, but case is ignored)
      * @return the shared translator instance
-     * @throws RuntimeException if no translator is configured for the provided name
+     * @throws RuntimeException
+     *             if no translator is configured for the provided name
      */
     Translator get(String name);
 
     /**
-     * Finds a {@link Translator} that is appropriate to the given type, which is usually obtained via {@link
-     * org.apache.tapestry5.Binding#getBindingType()}. Performs an inheritanced-based search for the best match.
-     *
-     * @param valueType the type of value for which a default translator is needed
+     * Finds a {@link Translator} that is appropriate to the given type, which is usually obtained via
+     * {@link org.apache.tapestry5.Binding#getBindingType()}. Performs an inheritance-based search for the best match,
+     * among the <em>standard</em> translator (not alternates).
+     * 
+     * @param valueType
+     *            the type of value for which a default translator is needed
      * @return the matching translator, or null if no match can be found
      */
     Translator findByType(Class valueType);
 
     /**
-     * Finds a {@link Translator} that is appropriate to the given type, which is usually obtained via {@link
-     * org.apache.tapestry5.Binding#getBindingType()}. Performs an inheritanced-based search for the best match.
-     *
-     * @param valueType the type of value for which a default translator is needed
+     * Finds a {@link Translator} that is appropriate to the given type, which is usually obtained via
+     * {@link org.apache.tapestry5.Binding#getBindingType()}. Performs an inheritance-based search for the best match,
+     * among the <em>standard</em> translators (not alternates).
+     * 
+     * @param valueType
+     *            the type of value for which a default translator is needed
      * @return the matching translator
-     * @throws IllegalArgumentException if no known validator matches the provided type
+     * @throws IllegalArgumentException
+     *             if no standard validator matches the provided type
      */
     Translator getByType(Class valueType);
 }

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/TranslatorAlternatesSourceImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/TranslatorAlternatesSourceImplTest.java?rev=939358&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/TranslatorAlternatesSourceImplTest.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/TranslatorAlternatesSourceImplTest.java Thu Apr 29 16:11:11 2010
@@ -0,0 +1,51 @@
+// Copyright 2010 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.services;
+
+import java.util.Map;
+
+import org.apache.tapestry5.Translator;
+import org.apache.tapestry5.internal.test.InternalBaseTestCase;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.testng.annotations.Test;
+
+@SuppressWarnings("unchecked")
+public class TranslatorAlternatesSourceImplTest extends InternalBaseTestCase
+{
+    @Test
+    public void name_mismatch()
+    {
+        Translator t = mockTranslator();
+
+        train_getName(t, "barney");
+
+        Map<String, Translator> configuration = CollectionFactory.newMap();
+
+        configuration.put("Fred", t);
+
+        replay();
+
+        try
+        {
+            new TranslatorAlternatesSourceImpl(configuration);
+            unreachable();
+        }
+        catch (RuntimeException ex)
+        {
+            assertEquals(ex.getMessage(),
+                    "Contribution key 'Fred' does not match 'barney' (the name of the corresponding Translator).");
+        }
+    }
+}

Propchange: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/TranslatorAlternatesSourceImplTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/TranslatorSourceImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/TranslatorSourceImplTest.java?rev=939358&r1=939357&r2=939358&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/TranslatorSourceImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/TranslatorSourceImplTest.java Thu Apr 29 16:11:11 2010
@@ -36,6 +36,7 @@ import org.testng.annotations.BeforeMeth
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
+@SuppressWarnings("unchecked")
 public class TranslatorSourceImplTest extends InternalBaseTestCase
 {
     private TranslatorSource source;
@@ -98,6 +99,82 @@ public class TranslatorSourceImplTest ex
     }
 
     @Test
+    public void name_collision_with_standard_translators()
+    {
+        Translator t1 = mockTranslator("fred", Integer.class);
+        Translator t2 = mockTranslator("fred", Long.class);
+
+        Map<Class, Translator> configuration = CollectionFactory.newMap();
+        configuration.put(Integer.class, t1);
+        configuration.put(Long.class, t2);
+
+        replay();
+
+        try
+        {
+            new TranslatorSourceImpl(configuration);
+            unreachable();
+        }
+        catch (RuntimeException ex)
+        {
+            assertMessageContains(
+                    ex,
+                    "Two different Translators contributed to the TranslatorSource service use the same translator name: 'fred'.",
+                    "Translator names must be unique.");
+        }
+
+        verify();
+    }
+
+    @Test
+    public void get_alternate_translator_by_name()
+    {
+        Translator t1 = mockTranslator("fred", Integer.class);
+        Translator t2 = mockTranslator();
+
+        Map<Class, Translator> configuration = newConfiguration(Integer.class, t1);
+
+        Map<String, Translator> alternates = CollectionFactory.newMap();
+        alternates.put("barney", t2);
+
+        replay();
+
+        TranslatorSource source = new TranslatorSourceImpl(configuration, alternates);
+
+        assertSame(source.get("barney"), t2);
+
+        verify();
+    }
+
+    @Test
+    public void name_collision_between_standard_and_alternate_translator()
+    {
+        Translator t1 = mockTranslator("fred", Integer.class);
+        Translator t2 = mockTranslator();
+
+        Map<Class, Translator> configuration = newConfiguration(Integer.class, t1);
+
+        Map<String, Translator> alternates = CollectionFactory.newMap();
+        alternates.put("fred", t2);
+
+        replay();
+
+        try
+        {
+            new TranslatorSourceImpl(configuration, alternates);
+            unreachable();
+        }
+        catch (RuntimeException ex)
+        {
+            assertEquals(
+                    ex.getMessage(),
+                    "Translator 'fred' contributed to the TranslatorAlternatesSource service has the same name as a standard Translator contributed to the TranslatorSource service.");
+        }
+
+        verify();
+    }
+
+    @Test
     public void unknown_translator_is_failure()
     {
         Translator fred = mockTranslator("fred", String.class);