You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2007/02/13 18:48:14 UTC

svn commit: r507118 - in /tapestry/tapestry5/tapestry-core/trunk/src: main/java/org/apache/tapestry/corelib/components/ main/java/org/apache/tapestry/internal/services/ site/apt/guide/ test/java/org/apache/tapestry/internal/services/

Author: hlship
Date: Tue Feb 13 09:48:13 2007
New Revision: 507118

URL: http://svn.apache.org/viewvc?view=rev&rev=507118
Log:
Add support for "?." separator inside property expressions, which will abort the read or write if the indicated term is null.
Remove logging decorators from built-in services.
Switch the Grid component back to using an inner class for its computed model binding.

Added:
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PropertyConduitSourceImplTest.java
Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Grid.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PropertyConduitSourceImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Grid.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Grid.java?view=diff&rev=507118&r1=507117&r2=507118
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Grid.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Grid.java Tue Feb 13 09:48:13 2007
@@ -82,60 +82,39 @@
     { "source=dataSource", "rowsPerPage=rowsPerPage", "currentPage=currentPage" })
     private GridPager _pager;
 
-    /**
-     * Dynamically computes the model based on the row type (determined from the
-     * {@link GridDataSource#getRowType() data model).  
-     */
-    static class ModelBinding extends AbstractBinding
+    Binding defaultModel()
     {
-        private final Grid _grid;
-
-        private final BeanModelSource _modelSource;
-
-        private final ComponentResources _containerResources;
-
-        public ModelBinding(final Grid grid, final BeanModelSource modelSource,
-                final ComponentResources containerResources)
-        {
-            _grid = grid;
-            _modelSource = modelSource;
-            _containerResources = containerResources;
-        }
-
-        public Object get()
-        {
-            // Get the default row type from the data source
+        final ComponentResources containerResources = _resources.getContainerResources();
 
-            Class rowType = _grid.getDataSource().getRowType();
-
-            if (rowType == null)
-                throw new RuntimeException("xxx -- no source to determine list type from");
-
-            // Properties do not have to be read/write
-
-            return _modelSource.create(rowType, false, _containerResources);
-        }
-
-        /**
-         * Returns false. This may be overkill, but it basically exists because the model is
-         * inherently mutable and therefore may contain client-specific state and needs to be
-         * discard at the end of the request. If the model were immutable, then we could leave
-         * invariant as true.
-         */
-        @Override
-        public boolean isInvariant()
+        return new AbstractBinding()
         {
-            return false;
-        }
-
-    }
-
-    Binding defaultModel()
-    {
-        // This should be done as an inner class, but Javassist has been barfing on that
-        // for some reason. This could be a problem!
 
-        return new ModelBinding(this, _modelSource, _resources.getContainerResources());
+            public Object get()
+            {
+                // Get the default row type from the data source
+
+                Class rowType = _dataSource.getRowType();
+
+                if (rowType == null)
+                    throw new RuntimeException("xxx -- no source to determine list type from");
+
+                // Properties do not have to be read/write
+
+                return _modelSource.create(rowType, false, containerResources);
+            }
+
+            /**
+             * Returns false. This may be overkill, but it basically exists because the model is
+             * inherently mutable and therefore may contain client-specific state and needs to be
+             * discarded at the end of the request. If the model were immutable, then we could leave
+             * invariant as true.
+             */
+            @Override
+            public boolean isInvariant()
+            {
+                return false;
+            }
+        };
     }
 
     Object setupRender()

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java?view=diff&rev=507118&r1=507117&r2=507118
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java Tue Feb 13 09:48:13 2007
@@ -40,11 +40,8 @@
 import org.apache.tapestry.ioc.annotations.Inject;
 import org.apache.tapestry.ioc.annotations.InjectService;
 import org.apache.tapestry.ioc.annotations.Lifecycle;
-import org.apache.tapestry.ioc.annotations.Match;
-import org.apache.tapestry.ioc.annotations.Order;
 import org.apache.tapestry.ioc.services.ChainBuilder;
 import org.apache.tapestry.ioc.services.ClassFactory;
-import org.apache.tapestry.ioc.services.LoggingDecorator;
 import org.apache.tapestry.ioc.services.PropertyAccess;
 import org.apache.tapestry.ioc.services.ThreadCleanupHub;
 import org.apache.tapestry.ioc.services.ThreadLocale;
@@ -224,21 +221,6 @@
     public static UpdateListenerHub buildUpdateListenerHub()
     {
         return new UpdateListenerHubImpl();
-    }
-
-    /**
-     * All public services in the tapestry module, and in any sub-module of tapestry will get
-     * logging. This doesn't include the tapesry.ioc module since services of that module can not be
-     * decorated.
-     */
-    @Match(
-    { "tapestry.*", "tapestry.*.*" })
-    @Order("before:*.*")
-    public static <T> T decorateWithLogging(Class<T> serviceInterface, T delegate,
-            String serviceId, Log log, @InjectService("tapestry.ioc.LoggingDecorator")
-            LoggingDecorator loggingDecorator)
-    {
-        return loggingDecorator.build(serviceInterface, delegate, serviceId, log);
     }
 
     @Lifecycle("perthread")

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PropertyConduitSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PropertyConduitSourceImpl.java?view=diff&rev=507118&r1=507117&r2=507118
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PropertyConduitSourceImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PropertyConduitSourceImpl.java Tue Feb 13 09:48:13 2007
@@ -173,6 +173,10 @@
             String thisStep = "step" + (i + 1);
             String term = terms[i];
 
+            boolean nullable = term.endsWith("?");
+            if (nullable)
+                term = term.substring(0, term.length() - 1);
+
             Method readMethod = readMethodForTerm(
                     activeType,
                     expression,
@@ -198,6 +202,9 @@
                     previousStep,
                     readMethod.getName());
 
+            if (nullable)
+                builder.addln("if (%s == null) return null;", thisStep);
+
             activeType = termType;
             result = readMethod;
             previousStep = thisStep;
@@ -237,6 +244,10 @@
             String thisStep = "step" + (i + 1);
             String term = terms[i];
 
+            boolean nullable = term.endsWith("?");
+            if (nullable)
+                term = term.substring(0, term.length() - 1);
+
             Method readMethod = readMethodForTerm(activeType, expression, term, true);
 
             // If a primitive type, convert to wrapper type
@@ -251,6 +262,9 @@
                     thisStep,
                     previousStep,
                     readMethod.getName());
+
+            if (nullable)
+                builder.addln("if (%s == null) return;", thisStep);
 
             activeType = termType;
             previousStep = thisStep;

Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt?view=diff&rev=507118&r1=507117&r2=507118
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/parameters.apt Tue Feb 13 09:48:13 2007
@@ -151,6 +151,11 @@
   This feature is most useful for accessing a couple of propertys of standard collection classes
   that aren't named as proper properties, such as Collection.size(), or Map.keySet().
       
+  Ever get frustrated, tripping over null pointers inside such an expression?  You may use
+  "?." instead of "." as the separator.  This adds a null check and aborts the expression
+  if the term is null.  Thus "foo?.bar?.baz" will return null if either foo or bar are null.
+  Likewise, updating "foo?.bar?.baz" will turn into a no-op if foo or bar is null.    
+      
   In addition, a few special cases are also supported. 
   In most cases, these special values save you the trouble of adding a "literal:" prefix to
   the value.  These special cases are <alternatives> to property expressions.

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PropertyConduitSourceImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PropertyConduitSourceImplTest.java?view=auto&rev=507118
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PropertyConduitSourceImplTest.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PropertyConduitSourceImplTest.java Tue Feb 13 09:48:13 2007
@@ -0,0 +1,79 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.services;
+
+import org.apache.tapestry.PropertyConduit;
+import org.apache.tapestry.internal.bindings.PropBindingFactoryTest;
+import org.apache.tapestry.internal.test.InternalBaseTestCase;
+import org.apache.tapestry.services.PropertyConduitSource;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/**
+ * Most of the testing occurs inside {@link PropBindingFactoryTest} (due to historical reasons).
+ */
+public class PropertyConduitSourceImplTest extends InternalBaseTestCase
+{
+    private PropertyConduitSource _source;
+
+    @BeforeClass
+    public void setup()
+    {
+        _source = getObject("infrastructure:PropertyConduitSource", PropertyConduitSource.class);
+    }
+
+    @AfterClass
+    public void cleanup()
+    {
+        _source = null;
+    }
+
+    @Test
+    public void question_dot_operator_for_object_type()
+    {
+        PropertyConduit normal = _source.create(CompositeBean.class, "simple.firstName");
+        PropertyConduit smart = _source.create(CompositeBean.class, "simple?.firstName");
+
+        CompositeBean bean = new CompositeBean();
+        bean.setSimple(null);
+
+        try
+        {
+            normal.get(bean);
+            unreachable();
+        }
+        catch (NullPointerException ex)
+        {
+            // Expected.
+        }
+
+        assertNull(smart.get(bean));
+
+        try
+        {
+            normal.set(bean, "Howard");
+            unreachable();
+        }
+        catch (NullPointerException ex)
+        {
+            // Expected.
+        }
+
+        // This will be a no-op due to the null property in the expression
+
+        smart.set(bean, "Howard");
+    }
+}