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
- * [EMAIL PROTECTED] 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 [EMAIL PROTECTED] 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");
+ }
+}