You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2010/01/22 17:29:35 UTC

svn commit: r902145 - 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: Fri Jan 22 16:29:34 2010
New Revision: 902145

URL: http://svn.apache.org/viewvc?rev=902145&view=rev
Log:
Add ability to inject the value of a component field from indirectly via ComponentValueProvider

Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetInjectionProvider.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ClassTransformation.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/AssetInjectionProviderTest.java

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetInjectionProvider.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetInjectionProvider.java?rev=902145&r1=902144&r2=902145&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetInjectionProvider.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetInjectionProvider.java Fri Jan 22 16:29:34 2010
@@ -1,10 +1,10 @@
-// Copyright 2007, 2008 The Apache Software Foundation
+// Copyright 2007, 2008, 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
+// 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,
@@ -14,6 +14,10 @@
 
 package org.apache.tapestry5.internal.services;
 
+import java.util.Locale;
+
+import org.apache.tapestry5.Asset;
+import org.apache.tapestry5.ComponentResources;
 import org.apache.tapestry5.annotations.Path;
 import org.apache.tapestry5.ioc.ObjectLocator;
 import org.apache.tapestry5.ioc.Resource;
@@ -21,13 +25,13 @@
 import org.apache.tapestry5.model.MutableComponentModel;
 import org.apache.tapestry5.services.AssetSource;
 import org.apache.tapestry5.services.ClassTransformation;
+import org.apache.tapestry5.services.ComponentValueProvider;
 import org.apache.tapestry5.services.InjectionProvider;
 
-import static java.lang.String.format;
-
 /**
- * Performs injection of assets, based on the presence of the {@link Path} annotation. This is more useful than the
- * general {@link AssetObjectProvider}, becase relative assets are supported.
+ * Performs injection of assets, based on the presence of the {@link Path} annotation. This is more
+ * useful than the
+ * general {@link AssetObjectProvider}, because relative assets are supported.
  */
 public class AssetInjectionProvider implements InjectionProvider
 {
@@ -42,28 +46,30 @@
     }
 
     public boolean provideInjection(String fieldName, Class fieldType, ObjectLocator locator,
-                                    ClassTransformation transformation, MutableComponentModel componentModel)
+            ClassTransformation transformation, MutableComponentModel componentModel)
     {
         Path path = transformation.getFieldAnnotation(fieldName, Path.class);
 
-        if (path == null) return false;
-
-        String expanded = symbolSource.expandSymbols(path.value());
-
-        String sourceFieldName = transformation.addInjectedField(AssetSource.class, "assetSource", assetSource);
-
-        String baseResourceFieldName = transformation.addInjectedField(Resource.class, "baseResource",
-                                                                       componentModel.getBaseResource());
-
-        String resourcesFieldName = transformation.getResourcesFieldName();
-
-        String statement = format("%s = (%s) %s.getAsset(%s, \"%s\", %s.getLocale());", fieldName, fieldType.getName(),
-                                  sourceFieldName, baseResourceFieldName, expanded, resourcesFieldName);
+        if (path == null)
+            return false;
 
-        transformation.extendConstructor(statement);
+        final String expanded = symbolSource.expandSymbols(path.value());
 
-        transformation.makeReadOnly(fieldName);
+        final Resource baseResource = componentModel.getBaseResource();
+        
+        ComponentValueProvider<Asset> provider = new ComponentValueProvider<Asset>()
+        {
+            @Override
+            public Asset get(ComponentResources resources)
+            {
+                Locale locale = resources.getLocale();
+
+                return assetSource.getAsset(baseResource, expanded, locale);
+            }
+        };
 
+        transformation.injectFieldIndirect(fieldName, provider);
+        
         return true;
     }
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java?rev=902145&r1=902144&r2=902145&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java Fri Jan 22 16:29:34 2010
@@ -14,9 +14,29 @@
 
 package org.apache.tapestry5.internal.services;
 
-import javassist.*;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Inherited;
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.Formatter;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtBehavior;
+import javassist.CtClass;
+import javassist.CtConstructor;
+import javassist.CtField;
+import javassist.CtMember;
+import javassist.CtMethod;
+import javassist.CtNewConstructor;
+import javassist.CtNewMethod;
+import javassist.NotFoundException;
 import javassist.expr.ExprEditor;
 import javassist.expr.FieldAccess;
+
 import org.apache.tapestry5.ComponentResources;
 import org.apache.tapestry5.internal.InternalComponentResources;
 import org.apache.tapestry5.ioc.internal.services.CtClassSource;
@@ -32,14 +52,14 @@
 import org.apache.tapestry5.model.ComponentModel;
 import org.apache.tapestry5.model.MutableComponentModel;
 import org.apache.tapestry5.runtime.Component;
-import org.apache.tapestry5.services.*;
+import org.apache.tapestry5.services.ComponentMethodAdvice;
+import org.apache.tapestry5.services.ComponentValueProvider;
+import org.apache.tapestry5.services.FieldFilter;
+import org.apache.tapestry5.services.MethodFilter;
+import org.apache.tapestry5.services.TransformMethodSignature;
+import org.apache.tapestry5.services.TransformUtils;
 import org.slf4j.Logger;
 
-import java.lang.annotation.Annotation;
-import java.lang.annotation.Inherited;
-import java.lang.reflect.Modifier;
-import java.util.*;
-
 /**
  * Implementation of the {@link org.apache.tapestry5.internal.services.InternalClassTransformation}
  * interface.
@@ -60,6 +80,8 @@
 
     private final IdAllocator idAllocator;
 
+    private final CtClass providerType;
+
     /**
      * Map, keyed on InjectKey, of field name. Injections are always added as protected (not
      * private) fields to support
@@ -146,6 +168,8 @@
         parentTransformation = null;
         this.componentModel = componentModel;
 
+        providerType = toCtClass(ComponentValueProvider.class);
+
         idAllocator = new IdAllocator();
 
         logger = componentModel.getLogger();
@@ -186,6 +210,8 @@
         this.parentTransformation = parentTransformation;
         this.componentModel = componentModel;
 
+        providerType = toCtClass(ComponentValueProvider.class);
+
         resourcesFieldName = parentTransformation.getResourcesFieldName();
 
         idAllocator = parentTransformation.getIdAllocator();
@@ -1261,19 +1287,15 @@
         String fieldName = addField(Modifier.PRIVATE | Modifier.FINAL, type.getName(),
                 suggestedName);
 
-        // TODO: This shouldn't have to be constantly recomputed
-
-        CtClass providerType = toCtClass(ComponentValueProvider.class);
-
-        constructorArgs.add(new ConstructorArg(providerType, provider));
+        String argName = addConstructorArg(providerType, provider);
 
         // Inside the constructor,
         // pass the resources to the provider's get() method, cast to the
         // field type and assign. This will likely not work with
         // primitives and arrays, but that's ok for now.
 
-        extendConstructor(String.format("  %s = (%s) $%d.get(%s);", fieldName, type.getName(),
-                constructorArgs.size(), resourcesFieldName));
+        extendConstructor(String.format("  %s = (%s) %s.get(%s);", fieldName, type.getName(),
+                argName, resourcesFieldName));
 
         return fieldName;
     }
@@ -1389,14 +1411,13 @@
      */
     private void addInjectToConstructor(String fieldName, CtClass fieldType, Object value)
     {
-        constructorArgs.add(new ConstructorArg(fieldType, value));
-
-        extendConstructor(String.format("  %s = $%d;", fieldName, constructorArgs.size()));
+        extendConstructor(String.format("  %s = %s;", fieldName,
+                addConstructorArg(fieldType, value)));
     }
 
     public void injectField(String fieldName, Object value)
     {
-        Defense.notNull(fieldName, "fieldName");
+        Defense.notBlank(fieldName, "fieldName");
 
         failIfFrozen();
 
@@ -1407,6 +1428,26 @@
         makeReadOnly(fieldName);
     }
 
+    @Override
+    public <T> void injectFieldIndirect(String fieldName, ComponentValueProvider<T> provider)
+    {
+        Defense.notBlank(fieldName, "fieldName");
+        Defense.notNull(provider, "provider");
+
+        failIfFrozen();
+
+        CtClass type = getFieldCtType(fieldName);
+
+        String argName = addConstructorArg(providerType, provider);
+
+        extendConstructor(String.format("  %s = (%s) %s.get(%s);", fieldName, type.getName(),
+                argName, resourcesFieldName));
+
+        // Add the provider to the constructor
+
+        makeReadOnly(fieldName);
+    }
+
     private CtClass convertNameToCtType(String type) throws NotFoundException
     {
         return classPool.get(type);
@@ -1928,4 +1969,20 @@
     {
         return parentTransformation == null;
     }
+
+    /**
+     * Adds a new constructor argument to the transformed constructor.
+     * 
+     * @param parameterType
+     *            type of parameter
+     * @param value
+     *            value of parameter
+     * @return psuedo-name of parameter (i.e., "$2", "$3", etc.)
+     */
+    private String addConstructorArg(CtClass parameterType, Object value)
+    {
+        constructorArgs.add(new ConstructorArg(parameterType, value));
+
+        return "$" + constructorArgs.size();
+    }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ClassTransformation.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ClassTransformation.java?rev=902145&r1=902144&r2=902145&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ClassTransformation.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ClassTransformation.java Fri Jan 22 16:29:34 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,
@@ -249,18 +249,24 @@
      * Like {@link #addInjectedField(Class, String, Object)}, but instead of specifying the value,
      * a provider for the value is specified. In the generated class' constructor, the provider
      * will be passed the {@link ComponentResources} and will return the final value; thus
-     * each component <em>instance</em> will receive a unique 
+     * each component <em>instance</em> will receive a unique
+     * 
      * @param <T>
-     * @param type type of value to inject
-     * @param suggestedName suggested name for the new field
-     * @param provider injected into the component to provide the value
+     * @param type
+     *            type of value to inject
+     * @param suggestedName
+     *            suggested name for the new field
+     * @param provider
+     *            injected into the component to provide the value
      * @return the actual name of the injected field
      * @since 5.2
      */
-    <T> String addIndirectInjectedField(Class<T> type, String suggestedName, ComponentValueProvider<T> provider);
-    
+    <T> String addIndirectInjectedField(Class<T> type, String suggestedName,
+            ComponentValueProvider<T> provider);
+
     /**
-     * Converts and <em>existing</em> field into a read only field whose value is the provided value. This is used
+     * Converts and <em>existing</em> field into a read only field whose value is the provided
+     * value. This is used
      * when converting an
      * existing field into a read-only injected value.
      * 
@@ -272,6 +278,22 @@
     void injectField(String fieldName, Object value);
 
     /**
+     * Like {@link #injectField(String, Object)}, except that the value to be injected is obtained
+     * from
+     * a {@link ComponentValueProvider}. It is assumed that the provider will return an object
+     * assignable to the field.
+     * 
+     * @param <T>
+     *            type of field
+     * @param fieldName
+     *            name of field to convert
+     * @param provider
+     *            provides the value to be assigned to the field
+     * @since 5.2.0
+     */
+    <T> void injectFieldIndirect(String fieldName, ComponentValueProvider<T> provider);
+
+    /**
      * Transforms the class to implement the indicated interface. If the class (or its super class)
      * does not already
      * implement the interface, then the interface is added, and default implementations of any

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/AssetInjectionProviderTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/AssetInjectionProviderTest.java?rev=902145&r1=902144&r2=902145&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/AssetInjectionProviderTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/AssetInjectionProviderTest.java Fri Jan 22 16:29:34 2010
@@ -1,4 +1,4 @@
-// Copyright 2007, 2008 The Apache Software Foundation
+// Copyright 2007, 2008, 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.
@@ -17,7 +17,6 @@
 import org.apache.tapestry5.annotations.Path;
 import org.apache.tapestry5.internal.test.InternalBaseTestCase;
 import org.apache.tapestry5.ioc.ObjectLocator;
-import org.apache.tapestry5.ioc.Resource;
 import org.apache.tapestry5.ioc.services.SymbolSource;
 import org.apache.tapestry5.model.MutableComponentModel;
 import org.apache.tapestry5.services.AssetSource;
@@ -48,51 +47,4 @@
 
         verify();
     }
-
-    @Test
-    public void path_annotation_present()
-    {
-        SymbolSource symbolSource = mockSymbolSource();
-        AssetSource assetSource = mockAssetSource();
-        ObjectLocator locator = mockObjectLocator();
-        ClassTransformation ct = mockClassTransformation();
-        MutableComponentModel model = mockMutableComponentModel();
-        Path annotation = mockPath();
-        Resource baseResource = mockResource();
-
-        String fieldName = "myField";
-        Class fieldType = Object.class;
-        String value = "${foo}";
-        String expanded = "foo.gif";
-
-        train_getFieldAnnotation(ct, fieldName, Path.class, annotation);
-
-        train_value(annotation, value);
-        train_expandSymbols(symbolSource, value, expanded);
-
-        train_addInjectedField(ct, AssetSource.class, "assetSource", assetSource, "as");
-
-        train_getBaseResource(model, baseResource);
-
-        train_addInjectedField(ct, Resource.class, "baseResource", baseResource, "br");
-
-        train_getResourcesFieldName(ct, "rez");
-
-        // This only tests that the code is generated as expected (which is a bit brittle), it
-        // doesn't prove that the generated code actually works, but we have lots of integration
-        // tests for that.
-
-        ct
-                .extendConstructor("myField = (java.lang.Object) as.getAsset(br, \"foo.gif\", rez.getLocale());");
-
-        ct.makeReadOnly(fieldName);
-
-        replay();
-
-        InjectionProvider provider = new AssetInjectionProvider(symbolSource, assetSource);
-
-        assertTrue(provider.provideInjection(fieldName, fieldType, locator, ct, model));
-
-        verify();
-    }
 }