Author: hlship
Date: Mon May 21 09:14:03 2007
New Revision: 540188

URL: http://svn.apache.org/viewvc?view=rev&rev=540188
Log:
TAPESTRY-1466: Support expansions inside ordinary attributes

Added:
    
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AttributeExpansionBinding.java
    
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/StringProvider.java
    
tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/AttributeExpansionsDemo.html
    
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/AttributeExpansionsDemo.java
    
tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app1/pages/AttributeExpansionsDemo.properties
Removed:
    
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/structure/AttributePageElement.java
Modified:
    
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageElementFactory.java
    
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageElementFactoryImpl.java
    
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java
    
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageLoaderProcessor.java
    
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java
    
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/BeanEditForm.html
    
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/GridRows.html
    
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/default.css
    
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties
    tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/templates.apt
    
tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/unit-testing-pages.apt
    tapestry/tapestry5/trunk/tapestry-core/src/site/apt/index.apt
    tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/Start.html
    
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
    
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PageElementFactoryImplTest.java
    
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PageLoaderImplTest.java

Added: 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AttributeExpansionBinding.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AttributeExpansionBinding.java?view=auto&rev=540188
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AttributeExpansionBinding.java
 (added)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AttributeExpansionBinding.java
 Mon May 21 09:14:03 2007
@@ -0,0 +1,40 @@
+// 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.Binding;
+import org.apache.tapestry.internal.bindings.AbstractBinding;
+import org.apache.tapestry.ioc.Location;
+
+/**
+ * Wraps a [EMAIL PROTECTED] StringProvider} as a read-only [EMAIL PROTECTED] 
Binding}.
+ */
+public class AttributeExpansionBinding extends AbstractBinding
+{
+    private final StringProvider _provider;
+
+    public AttributeExpansionBinding(StringProvider provider, Location 
location)
+    {
+        super(location);
+
+        _provider = provider;
+    }
+
+    public Object get()
+    {
+        return _provider.provideString();
+    }
+
+}

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageElementFactory.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageElementFactory.java?view=diff&rev=540188&r1=540187&r2=540188
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageElementFactory.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageElementFactory.java
 Mon May 21 09:14:03 2007
@@ -14,6 +14,7 @@
 
 package org.apache.tapestry.internal.services;
 
+import org.apache.tapestry.Binding;
 import org.apache.tapestry.ComponentResources;
 import org.apache.tapestry.annotations.Component;
 import org.apache.tapestry.internal.parser.AttributeToken;
@@ -26,6 +27,7 @@
 import org.apache.tapestry.internal.structure.Page;
 import org.apache.tapestry.internal.structure.PageElement;
 import org.apache.tapestry.ioc.Location;
+import org.apache.tapestry.services.BindingSource;
 
 /**
  * Used by the [EMAIL PROTECTED] 
org.apache.tapestry.internal.services.PageLoader} to create page elements
@@ -36,11 +38,21 @@
 
     PageElement newStartElement(StartElementToken token);
 
-    PageElement newAttributeElement(AttributeToken token);
+    PageElement newAttributeElement(ComponentResources componentResources, 
AttributeToken token);
 
     PageElement newEndElement();
 
     PageElement newExpansionElement(ComponentResources componentResources, 
ExpansionToken token);
+
+    /**
+     * Creates a new binding as with
+     * [EMAIL PROTECTED] BindingSource#newBinding(String, ComponentResources, 
ComponentResources, String, String, Location)}.
+     * However, if the binding contains an expansion (i.e., 
<code>${...}</code>), then a binding
+     * that returns the fully expanded expression will be returned.
+     */
+    Binding newBinding(String parameterName, ComponentResources 
loadingComponentResources,
+            ComponentResources embeddedComponentResources, String 
defaultBindingPrefix,
+            String expression, Location location);
 
     /**
      * Creates a new component and adds it to the page and to its container.

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageElementFactoryImpl.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageElementFactoryImpl.java?view=diff&rev=540188&r1=540187&r2=540188
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageElementFactoryImpl.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageElementFactoryImpl.java
 Mon May 21 09:14:03 2007
@@ -14,17 +14,20 @@
 
 package org.apache.tapestry.internal.services;
 
+import static org.apache.tapestry.TapestryConstants.PROP_BINDING_PREFIX;
+import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
+
+import java.util.List;
+
 import org.apache.tapestry.Binding;
 import org.apache.tapestry.ComponentResources;
 import org.apache.tapestry.MarkupWriter;
-import org.apache.tapestry.TapestryConstants;
 import org.apache.tapestry.internal.parser.AttributeToken;
 import org.apache.tapestry.internal.parser.CommentToken;
 import org.apache.tapestry.internal.parser.DTDToken;
 import org.apache.tapestry.internal.parser.ExpansionToken;
 import org.apache.tapestry.internal.parser.StartElementToken;
 import org.apache.tapestry.internal.parser.TextToken;
-import org.apache.tapestry.internal.structure.AttributePageElement;
 import org.apache.tapestry.internal.structure.CommentPageElement;
 import org.apache.tapestry.internal.structure.ComponentPageElement;
 import org.apache.tapestry.internal.structure.ComponentPageElementImpl;
@@ -56,6 +59,23 @@
 
     private final ComponentMessagesSource _messagesSource;
 
+    private static final String EXPANSION_START = "${";
+
+    private static class LiteralStringProvider implements StringProvider
+    {
+        private final String _string;
+
+        LiteralStringProvider(String string)
+        {
+            _string = string;
+        }
+
+        public String provideString()
+        {
+            return _string;
+        }
+    }
+
     public PageElementFactoryImpl(ComponentInstantiatorSource 
componentInstantiatorSource,
             ComponentClassResolver resolver, TypeCoercer typeCoercer, 
BindingSource bindingSource,
             ComponentMessagesSource messagesSource)
@@ -97,9 +117,109 @@
         return _endElement;
     }
 
-    public PageElement newAttributeElement(AttributeToken token)
+    public PageElement newAttributeElement(ComponentResources 
componentResources,
+            final AttributeToken token)
     {
-        return new AttributePageElement(token.getName(), token.getValue());
+        final StringProvider provider = parseAttributeExpansionExpression(
+                token.getValue(),
+                componentResources,
+                token.getLocation());
+
+        final String name = token.getName();
+
+        return new PageElement()
+        {
+            public void render(MarkupWriter writer, RenderQueue queue)
+            {
+                writer.attributes(name, provider.provideString());
+            }
+        };
+    }
+
+    private StringProvider parseAttributeExpansionExpression(String expression,
+            ComponentResources resources, final Location location)
+    {
+        final List<StringProvider> providers = newList();
+
+        int startx = 0;
+
+        while (true)
+        {
+            int expansionx = expression.indexOf(EXPANSION_START, startx);
+
+            // No more expansions, add in the rest of the string as a literal.
+
+            if (expansionx < 0)
+            {
+                if (startx < expression.length())
+                    providers.add(new 
LiteralStringProvider(expression.substring(startx)));
+                break;
+            }
+
+            // Add in a literal string chunk for the characters between the 
last expansion and
+            // this expansion.
+
+            if (startx != expansionx)
+                providers.add(new 
LiteralStringProvider(expression.substring(startx, expansionx)));
+
+            int endx = expression.indexOf("}", expansionx);
+
+            if (endx < 0)
+                throw new TapestryException(ServicesMessages
+                        .unclosedAttributeExpression(expression), location, 
null);
+
+            String expansion = expression.substring(expansionx + 2, endx);
+
+            final Binding binding = _bindingSource.newBinding(
+                    "attribute expansion",
+                    resources,
+                    resources,
+                    PROP_BINDING_PREFIX,
+                    expansion,
+                    location);
+
+            final StringProvider provider = new StringProvider()
+            {
+                public String provideString()
+                {
+                    try
+                    {
+                        Object raw = binding.get();
+
+                        return _typeCoercer.coerce(raw, String.class);
+                    }
+                    catch (Exception ex)
+                    {
+                        throw new TapestryException(ex.getMessage(), location, 
ex);
+                    }
+                }
+            };
+
+            providers.add(provider);
+
+            // Restart the search after '}'
+
+            startx = endx + 1;
+        }
+
+        // Simplify the typical case, where the entire attribute is just a 
single expansion:
+
+        if (providers.size() == 1) return providers.get(0);
+
+        return new StringProvider()
+        {
+
+            public String provideString()
+            {
+                StringBuilder builder = new StringBuilder();
+
+                for (StringProvider provider : providers)
+                    builder.append(provider.provideString());
+
+                return builder.toString();
+            }
+        };
+
     }
 
     public PageElement newExpansionElement(ComponentResources 
componentResources,
@@ -109,7 +229,7 @@
                 "expansion",
                 componentResources,
                 componentResources,
-                TapestryConstants.PROP_BINDING_PREFIX,
+                PROP_BINDING_PREFIX,
                 token.getExpression(),
                 token.getLocation());
 
@@ -255,5 +375,29 @@
     public PageElement newDTDElement(DTDToken token)
     {
         return new DTDPageElement(token.getName(), token.getPublicId(), 
token.getSystemId());
+    }
+
+    public Binding newBinding(String parameterName, ComponentResources 
loadingComponentResources,
+            ComponentResources embeddedComponentResources, String 
defaultBindingPrefix,
+            String expression, Location location)
+    {
+
+        if (expression.contains(EXPANSION_START))
+        {
+            StringProvider provider = parseAttributeExpansionExpression(
+                    expression,
+                    loadingComponentResources,
+                    location);
+
+            return new AttributeExpansionBinding(provider, location);
+        }
+
+        return _bindingSource.newBinding(
+                "parameter " + parameterName,
+                loadingComponentResources,
+                embeddedComponentResources,
+                defaultBindingPrefix,
+                expression,
+                location);
     }
 }

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java?view=diff&rev=540188&r1=540187&r2=540188
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java
 Mon May 21 09:14:03 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 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.
@@ -19,7 +19,6 @@
 import org.apache.tapestry.events.InvalidationListener;
 import org.apache.tapestry.internal.event.InvalidationEventHubImpl;
 import org.apache.tapestry.internal.structure.Page;
-import org.apache.tapestry.services.BindingSource;
 import org.apache.tapestry.services.PersistentFieldManager;
 
 public class PageLoaderImpl extends InvalidationEventHubImpl implements 
PageLoader,
@@ -29,19 +28,17 @@
 
     private final PageElementFactory _pageElementFactory;
 
-    private final BindingSource _bindingSource;
-
     private final LinkFactory _linkFactory;
 
     private final PersistentFieldManager _persistentFieldManager;
 
     public PageLoaderImpl(ComponentTemplateSource templateSource,
-            PageElementFactory pageElementFactory, BindingSource bindingSource,
-            LinkFactory linkFactory, PersistentFieldManager 
persistentFieldManager)
+            PageElementFactory pageElementFactory, LinkFactory linkFactory,
+            PersistentFieldManager persistentFieldManager)
     {
         _templateSource = templateSource;
         _pageElementFactory = pageElementFactory;
-        _bindingSource = bindingSource;
+
         _linkFactory = linkFactory;
         _persistentFieldManager = persistentFieldManager;
     }
@@ -57,7 +54,7 @@
         // effort to pool them for reuse, but not too likely.
 
         PageLoaderProcessor processor = new 
PageLoaderProcessor(_templateSource,
-                _pageElementFactory, _bindingSource, _linkFactory, 
_persistentFieldManager);
+                _pageElementFactory, _linkFactory, _persistentFieldManager);
 
         return processor.loadPage(pageClassName, locale);
     }

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageLoaderProcessor.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageLoaderProcessor.java?view=diff&rev=540188&r1=540187&r2=540188
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageLoaderProcessor.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageLoaderProcessor.java
 Mon May 21 09:14:03 2007
@@ -79,8 +79,6 @@
 
     private boolean _dtdAdded;
 
-    private final BindingSource _bindingSource;
-
     private final Stack<BodyPageElement> _bodyPageElementStack = newStack();
 
     // You can use a stack as a queue
@@ -113,12 +111,11 @@
     private final ComponentTemplateSource _templateSource;
 
     public PageLoaderProcessor(ComponentTemplateSource templateSource,
-            PageElementFactory pageElementFactory, BindingSource bindingSource,
-            LinkFactory linkFactory, PersistentFieldManager 
persistentFieldManager)
+            PageElementFactory pageElementFactory, LinkFactory linkFactory,
+            PersistentFieldManager persistentFieldManager)
     {
         _templateSource = templateSource;
         _pageElementFactory = pageElementFactory;
-        _bindingSource = bindingSource;
         _linkFactory = linkFactory;
         _persistentFieldManager = persistentFieldManager;
     }
@@ -244,8 +241,8 @@
             return new InheritedBinding(description, existing, location);
         }
 
-        return _bindingSource.newBinding(
-                "parameter " + name,
+        return _pageElementFactory.newBinding(
+                name,
                 loadingComponent.getComponentResources(),
                 component.getComponentResources(),
                 defaultBindingPrefix,
@@ -295,7 +292,8 @@
             return;
         }
 
-        PageElement element = _pageElementFactory.newAttributeElement(token);
+        PageElement element = 
_pageElementFactory.newAttributeElement(_loadingElement
+                .getComponentResources(), token);
 
         addToBody(element);
     }

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java?view=diff&rev=540188&r1=540187&r2=540188
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java
 Mon May 21 09:14:03 2007
@@ -388,4 +388,9 @@
     {
         return MESSAGES.get("corrupt-client-state");
     }
+
+    static String unclosedAttributeExpression(String expression)
+    {
+        return MESSAGES.format("unclosed-attribute-expression", expression);
+    }
 }

Added: 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/StringProvider.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/StringProvider.java?view=auto&rev=540188
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/StringProvider.java
 (added)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/StringProvider.java
 Mon May 21 09:14:03 2007
@@ -0,0 +1,27 @@
+// 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;
+
+/**
+ * Interface used when assembling an attribute value that contains expansions.
+ */
+public interface StringProvider
+{
+    /**
+     * Ask the object to provide the desired string. Often this involves 
computing the string value
+     * dynamically, or aggregating together multiple StringProviders.
+     */
+    String provideString();
+}
\ No newline at end of file

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/BeanEditForm.html
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/BeanEditForm.html?view=diff&rev=540188&r1=540187&r2=540188
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/BeanEditForm.html
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/BeanEditForm.html
 Mon May 21 09:14:03 2007
@@ -7,7 +7,7 @@
             <t:delegate to="blockForProperty"/>
         </div>
         <div class="t-beaneditor-row">
-            <input t:type="Submit" type="submit" value="prop:submitLabel"/>
+            <input type="submit" value="${submitLabel}"/>
         </div>
     </div>
     

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/GridRows.html
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/GridRows.html?view=diff&rev=540188&r1=540187&r2=540188
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/GridRows.html
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/GridRows.html
 Mon May 21 09:14:03 2007
@@ -1,6 +1,6 @@
 <tr t:id="row" class="prop:rowClass" 
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd";>
     <t:loop source="propertyNames" value="propertyName" volatile="true">
-        <td t:id="cell" class="prop:cellClass">
+        <td class="${cellClass}">
              <t:gridcell model="columnModel" row="row" 
resources="componentResources.containerResources"/>
         </td>        
     </t:loop>

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/default.css
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/default.css?view=diff&rev=540188&r1=540187&r2=540188
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/default.css
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/default.css
 Mon May 21 09:14:03 2007
@@ -271,7 +271,7 @@
 {
   width: 40px;
   text-align: right;
-  padding: none;
+  padding: 0px;
   background-color: #E1E1E1;
   padding-right: 3px; 
   border-right: 1px solid black;
@@ -292,4 +292,51 @@
 TD.t-location-content-first
 {
   border-top: 1px solid black;
+}
+
+DIV.t-palette {
+  display: inline;
+}
+
+DIV.t-palette SELECT {
+  margin-bottom: 2px;
+}
+
+DIV.t-palette-title {
+  color: white;
+  background-color: #809FFF;
+  text-align: center;
+  font-weight: bold;
+  margin-bottom: 3px;
+  display: block;
+}
+
+
+DIV.t-palette-available {
+  float: left;
+}
+
+DIV.t-palette-controls { 
+    margin: 0px 5px;
+       width: 64px;
+       float: left;
+       text-align: center;
+}
+
+DIV.t-palette-controls BUTTON { 
+  width: 64px;
+}
+
+DIV.t-palette-controls BUTTON[disabled] IMG  {
+  filter: alpha(opacity=25);
+  -moz-opacity: .25;
+}
+
+DIV.t-palette-selected {
+  float: left;
+  clear: right;
+}
+
+DIV.t-palette-spacer {
+  clear: left;
 }

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties?view=diff&rev=540188&r1=540187&r2=540188
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties
 Mon May 21 09:14:03 2007
@@ -76,4 +76,5 @@
 field-injection-error=Error obtaining injected value for field %s.%s: %s
 client-state-must-be-serializable=State persisted on the client must be 
serializable, but %s does not implement the Serializable interface.
 corrupt-client-state=Serialized client state was corrupted. \
-  This may indicate that too much state is being stored, which can cause the 
encoded string to be truncated by the client web browser.
\ No newline at end of file
+  This may indicate that too much state is being stored, which can cause the 
encoded string to be truncated by the client web browser.
+unclosed-attribute-expression=Attribute expression '%s' is missing a closing 
brace.

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/templates.apt
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/templates.apt?view=diff&rev=540188&r1=540187&r2=540188
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/templates.apt 
(original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/templates.apt Mon 
May 21 09:14:03 2007
@@ -163,8 +163,15 @@
   Here, <<<$\{userId}>>> is the expansion.  In this example, the userId 
property of the 
   component is extracted, converted to a string, and streamed into the output. 
 
  
-  Expansions may only occur in ordinary template text; they are not allowed 
inside
-  attributes, or inside CDATA sections.
+  Expansions are allowed inside text, and inside attributes of ordinary 
elements, and component elements.  For example:
+  
++---+
+  <img src="${request.contextPath}/images/catalog/product_${productId}"/>
++---+
+
+  In this hypothetical example, the component class is providing a request 
property and a productId property, and these are being
+  used inside the template to assemble the src attribute of the \<img\> 
element.  This is component-like behavior without
+  actual components.
   
   Under the covers, expansions are the same as
   {{{parameters.html}parameter bindings}}.  The default

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/unit-testing-pages.apt
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/unit-testing-pages.apt?view=diff&rev=540188&r1=540187&r2=540188
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/unit-testing-pages.apt
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/site/apt/guide/unit-testing-pages.apt
 Mon May 21 09:14:03 2007
@@ -136,37 +136,4 @@
 
   To unit test a component, just create a test page containing that component. 
Then 
   unit test that page.
-  
-* Providing mock implementations for your service layer
 
-  Your page typically will invoke your business logic and will ultimately reach
-  the database. In unit testing, it is highly recommended that you not invoke
-  them as you're unit testing the UI (page). To do that, you should put 
-  your transaction control code, business logic and etc into Tapestry IoC
-  services. Then you should override their implementations using mock objects:
-
-+---+
-public class MyTest extends Assert
-{
-    @Test
-    public void test1()
-    {
-        String appPackage = "org.example.app";
-        String appName = "LocaleApp";
-        Map<String, Object> serviceOverrides = new HashMap<String, Object>();
-        Service1 service1Mock = new Service1() {
-            public Object transform(Object input) {
-                //assert against the input and return some mock value
-            }
-        }
-        Service2 service2Mock = new Service2() {
-            public void perform(Object cmd) {
-                //assert against the cmd
-            }
-        }
-        serviceOverrides.put("Service1", service1Mock);
-        serviceOverrides.put("Service2", service2Mock);
-        PageTester tester = new PageTester(appPackage, appName, 
"src/main/webapp", serviceOverrides);
-    }
-}
-+---+

Modified: tapestry/tapestry5/trunk/tapestry-core/src/site/apt/index.apt
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/site/apt/index.apt?view=diff&rev=540188&r1=540187&r2=540188
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/site/apt/index.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/site/apt/index.apt Mon May 21 
09:14:03 2007
@@ -13,6 +13,11 @@
   Progress on Tapestry 5 is really taking off. This space lists some cool new 
features that have been added
   recently.
   
+  * Template expansions are now allowed inside attributes.
+  
+  * Applications may optionally provide a global message catalog (which is 
searched when a page or component message catalog
+    can not directly provide a localized message).
+  
   * Explicit \<!DOCTYPE\> declarations inside page and component templates 
will now be forwarded through to the client
     web browser.
     

Added: 
tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/AttributeExpansionsDemo.html
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/AttributeExpansionsDemo.html?view=auto&rev=540188
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/AttributeExpansionsDemo.html
 (added)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/AttributeExpansionsDemo.html
 Mon May 21 09:14:03 2007
@@ -0,0 +1,25 @@
+<t:border  xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd";>
+  
+  <h1>Expansions in Attributes</h1>
+  
+  <style>
+    DIV.red { font-weight: bold; color: red; }
+    
+    DIV.goober-red { font-style: italic; color: #FF9F2F; }
+ 
+    DIV.goober-green { font-size: x-large; color: green; }
+    
+  </style>
+  
+  <div id="mixed-expansion" style="color: ${colorscheme}">This text is blue, 
thanks to property colorscheme.</div>
+  
+  <div id="single" class="${styleClass}">This text is red and bold, thanks to 
the class attribute, set from property styleClass.</div>
+  
+  <div id="consecutive" class="${message:cssclassprefix}${styleClass}">And 
this text is italic and orange, from a complex CSS class attribute, built from 
multiple expansions.</div>
+  
+  <div id="trailer" class="${message:cssclassprefix}green">This text is green 
and large, and tests a trailing literal string within an attribute with an 
expansion.</div>
+  
+  <div id="formal"><t:echo value="${prefix}${message:alert}"/></div>
+  
+  <!-- A comment! -->
+</t:border>
\ No newline at end of file

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/Start.html
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/Start.html?view=diff&rev=540188&r1=540187&r2=540188
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/Start.html 
(original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/Start.html Mon 
May 21 09:14:03 2007
@@ -134,6 +134,9 @@
           <li>
             <t:pagelink page="ClientPersistenceDemo">Client Persistence 
Demo</t:pagelink> -- component field values persisted on the client side
           </li>
+          <li>
+            <t:pagelink page="attributeExpansionsDemo">Attribute Expansions 
Demo</t:pagelink> -- use expansions inside attributes of ordinary elements
+          </li>
         </ul>
       </td>
     </tr>

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java?view=diff&rev=540188&r1=540187&r2=540188
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
 Mon May 21 09:14:03 2007
@@ -563,7 +563,7 @@
     @Test
     public void bean_editor()
     {
-        String submitButton = "//[EMAIL PROTECTED]'submit']";
+        String submitButton = "//[EMAIL PROTECTED]'submit']";
 
         open(BASE_URL);
         clickAndWait("link=BeanEditor Demo");
@@ -582,7 +582,7 @@
 
         // Check override of the submit label
 
-        assertText("//[EMAIL PROTECTED]'submit']/@value", "Register");
+        assertText("//[EMAIL PROTECTED]'submit']/@value", "Register");
 
         type("firstName", "a");
         type("lastName", "b");
@@ -819,7 +819,7 @@
 
         // Notice: click, not click and wait.
 
-        click("submit");
+        click("//[EMAIL PROTECTED]'submit']");
 
         assertTextSeries(
                 "//li[%d]",
@@ -831,14 +831,14 @@
         type("firstName", "Howard");
         type("lastName", "Lewis Ship");
         type("birthYear", "1000");
-        click("submit");
+        click("//[EMAIL PROTECTED]'submit']");
 
         assertText("//li", "Year of Birth requires a value of at least 1900.");
 
         type("birthYear", "1966");
         click("citizen");
 
-        clickAndWait("submit");
+        clickAndWait("//[EMAIL PROTECTED]'submit']");
 
         assertTextPresent("First Name: [Howard]");
     }
@@ -925,5 +925,24 @@
         clickAndWait("link=store string");
 
         assertTextPresent("Persisted value: [A String]", "Session: [false]");
+    }
+
+    @Test
+    public void attribute_expansions()
+    {
+        open(BASE_URL);
+        clickAndWait("link=Attribute Expansions Demo");
+
+        assertText("//[EMAIL PROTECTED]'mixed-expansion']/@style", "color: 
blue;");
+        assertText("//[EMAIL PROTECTED]'single']/@class", "red");
+        assertText("//[EMAIL PROTECTED]'consecutive']/@class", "goober-red");
+        assertText("//[EMAIL PROTECTED]'trailer']/@class", "goober-green");
+        assertText(
+                "//[EMAIL PROTECTED]'formal']/text()",
+                "ALERT-expansions work inside formal component parameters as 
well");
+
+        // An unrelated test, but fills in a bunch of minor gaps.
+
+        assertSourcePresent("<!-- A comment! -->");
     }
 }

Added: 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/AttributeExpansionsDemo.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/AttributeExpansionsDemo.java?view=auto&rev=540188
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/AttributeExpansionsDemo.java
 (added)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/AttributeExpansionsDemo.java
 Mon May 21 09:14:03 2007
@@ -0,0 +1,33 @@
+// 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.integration.app1.pages;
+
+public class AttributeExpansionsDemo
+{
+    public String getColorScheme()
+    {
+        return "blue";
+    }
+
+    public String getStyleClass()
+    {
+        return "red";
+    }
+
+    public String getPrefix()
+    {
+        return "ALERT-";
+    }
+}

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PageElementFactoryImplTest.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PageElementFactoryImplTest.java?view=diff&rev=540188&r1=540187&r2=540188
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PageElementFactoryImplTest.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PageElementFactoryImplTest.java
 Mon May 21 09:14:03 2007
@@ -12,166 +12,204 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.internal.services;
-
-import org.apache.tapestry.MarkupWriter;
-import org.apache.tapestry.dom.MarkupModel;
-import org.apache.tapestry.dom.XMLMarkupModel;
-import org.apache.tapestry.internal.parser.AttributeToken;
-import org.apache.tapestry.internal.parser.StartElementToken;
-import org.apache.tapestry.internal.parser.TextToken;
-import org.apache.tapestry.internal.structure.ComponentPageElement;
-import org.apache.tapestry.internal.structure.PageElement;
-import org.apache.tapestry.internal.test.InternalBaseTestCase;
+package org.apache.tapestry.internal.services;
+
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.MarkupWriter;
+import org.apache.tapestry.dom.MarkupModel;
+import org.apache.tapestry.dom.XMLMarkupModel;
+import org.apache.tapestry.internal.parser.AttributeToken;
+import org.apache.tapestry.internal.parser.StartElementToken;
+import org.apache.tapestry.internal.parser.TextToken;
+import org.apache.tapestry.internal.structure.ComponentPageElement;
+import org.apache.tapestry.internal.structure.PageElement;
+import org.apache.tapestry.internal.test.InternalBaseTestCase;
 import org.apache.tapestry.ioc.Location;
-import org.apache.tapestry.runtime.RenderQueue;
-import org.apache.tapestry.services.ComponentClassResolver;
-import org.testng.annotations.Test;
-
-public class PageElementFactoryImplTest extends InternalBaseTestCase
-{
-    private static MarkupModel _xmlModel = new XMLMarkupModel();
-
-    @Test
-    public void start_element()
-    {
-        ComponentInstantiatorSource source = mockComponentInstantiatorSource();
-        ComponentClassResolver resolver = mockComponentClassResolver();
-        MarkupWriter writer = new MarkupWriterImpl();
-        Location l = mockLocation();
-        RenderQueue queue = mockRenderQueue();
-
-        replay();
-
-        PageElementFactory factory = new PageElementFactoryImpl(source, 
resolver, null, null, null);
-        StartElementToken token = new StartElementToken("fred", l);
-
-        PageElement element = factory.newStartElement(token);
-
-        element.render(writer, queue);
-
-        verify();
-
-        assertEquals(element.toString(), "Start[fred]");
-        assertEquals(writer.toString(), "<fred></fred>");
-    }
-
-    @Test
-    public void attribute()
-    {
-        ComponentInstantiatorSource source = mockComponentInstantiatorSource();
-        ComponentClassResolver resolver = mockComponentClassResolver();
-        MarkupWriter writer = new MarkupWriterImpl(_xmlModel, null);
-        Location l = mockLocation();
-        RenderQueue queue = mockRenderQueue();
-
-        replay();
-
-        PageElementFactory factory = new PageElementFactoryImpl(source, 
resolver, null, null, null);
-        AttributeToken token = new AttributeToken("name", "value", l);
-
-        PageElement element = factory.newAttributeElement(token);
-
-        writer.element("root");
-
-        element.render(writer, queue);
-
-        verify();
-
-        assertEquals(element.toString(), "Attribute[name=value]");
-        assertEquals(writer.toString(), "<root name=\"value\"/>");
-    }
-
-    @Test
-    public void end_element()
-    {
-        ComponentInstantiatorSource source = mockComponentInstantiatorSource();
-        ComponentClassResolver resolver = mockComponentClassResolver();
-        MarkupWriter writer = new MarkupWriterImpl(_xmlModel, null);
-        RenderQueue queue = mockRenderQueue();
-
-        replay();
-
-        PageElementFactory factory = new PageElementFactoryImpl(source, 
resolver, null, null, null);
-
-        PageElement element = factory.newEndElement();
-
-        writer.element("root");
-        writer.write("before");
-        writer.element("nested");
-
-        element.render(writer, queue);
-
-        writer.write("after");
-
-        verify();
-
-        assertEquals(element.toString(), "End");
-        assertEquals(writer.toString(), "<root>before<nested/>after</root>");
-    }
-
-    @Test
-    public void end_element_is_singleton()
-    {
-        ComponentInstantiatorSource source = mockComponentInstantiatorSource();
-        ComponentClassResolver resolver = mockComponentClassResolver();
-
-        replay();
-
-        PageElementFactory factory = new PageElementFactoryImpl(source, 
resolver, null, null, null);
-
-        PageElement element1 = factory.newEndElement();
-        PageElement element2 = factory.newEndElement();
-
-        assertSame(element2, element1);
-
-        verify();
-    }
-
-    @Test
-    public void text_element()
-    {
-        ComponentInstantiatorSource source = mockComponentInstantiatorSource();
-        ComponentClassResolver resolver = mockComponentClassResolver();
-        MarkupWriter writer = new MarkupWriterImpl();
-        Location l = mockLocation();
-        RenderQueue queue = mockRenderQueue();
-
-        replay();
-
-        PageElementFactory factory = new PageElementFactoryImpl(source, 
resolver, null, null, null);
-        TextToken token = new TextToken("some text", l);
-
-        PageElement element = factory.newTextElement(token);
-
-        writer.element("root");
-        element.render(writer, queue);
-
-        verify();
-
-        assertEquals(element.toString(), "Text[some text]");
-        assertEquals(writer.toString(), "<root>some text</root>");
-    }
-
-    @Test
-    public void render_body_element()
-    {
-        ComponentInstantiatorSource source = mockComponentInstantiatorSource();
-        ComponentClassResolver resolver = mockComponentClassResolver();
-        RenderQueue queue = mockRenderQueue();
-        ComponentPageElement component = newMock(ComponentPageElement.class);
-        MarkupWriter writer = newMock(MarkupWriter.class);
-
-        component.enqueueBeforeRenderBody(queue);
-
-        replay();
-
-        PageElementFactory factory = new PageElementFactoryImpl(source, 
resolver, null, null, null);
-
-        PageElement element = factory.newRenderBodyElement(component);
-
-        element.render(writer, queue);
-
-        verify();
-    }
-}
+import org.apache.tapestry.ioc.internal.util.TapestryException;
+import org.apache.tapestry.ioc.services.TypeCoercer;
+import org.apache.tapestry.runtime.RenderQueue;
+import org.apache.tapestry.services.BindingSource;
+import org.apache.tapestry.services.ComponentClassResolver;
+import org.apache.tapestry.services.ComponentMessagesSource;
+import org.testng.annotations.Test;
+
+public class PageElementFactoryImplTest extends InternalBaseTestCase
+{
+    private static MarkupModel _xmlModel = new XMLMarkupModel();
+
+    @Test
+    public void start_element()
+    {
+        ComponentInstantiatorSource source = mockComponentInstantiatorSource();
+        ComponentClassResolver resolver = mockComponentClassResolver();
+        MarkupWriter writer = new MarkupWriterImpl();
+        Location l = mockLocation();
+        RenderQueue queue = mockRenderQueue();
+
+        replay();
+
+        PageElementFactory factory = new PageElementFactoryImpl(source, 
resolver, null, null, null);
+        StartElementToken token = new StartElementToken("fred", l);
+
+        PageElement element = factory.newStartElement(token);
+
+        element.render(writer, queue);
+
+        verify();
+
+        assertEquals(element.toString(), "Start[fred]");
+        assertEquals(writer.toString(), "<fred></fred>");
+    }
+
+    @Test
+    public void attribute()
+    {
+        ComponentInstantiatorSource source = mockComponentInstantiatorSource();
+        ComponentClassResolver resolver = mockComponentClassResolver();
+        MarkupWriter writer = new MarkupWriterImpl(_xmlModel, null);
+        Location l = mockLocation();
+        RenderQueue queue = mockRenderQueue();
+
+        replay();
+
+        PageElementFactory factory = new PageElementFactoryImpl(source, 
resolver, null, null, null);
+        AttributeToken token = new AttributeToken("name", "value", l);
+
+        PageElement element = factory.newAttributeElement(null, token);
+
+        writer.element("root");
+
+        element.render(writer, queue);
+
+        verify();
+
+        assertEquals(writer.toString(), "<root name=\"value\"/>");
+    }
+
+    @Test
+    public void end_element()
+    {
+        ComponentInstantiatorSource source = mockComponentInstantiatorSource();
+        ComponentClassResolver resolver = mockComponentClassResolver();
+        MarkupWriter writer = new MarkupWriterImpl(_xmlModel, null);
+        RenderQueue queue = mockRenderQueue();
+
+        replay();
+
+        PageElementFactory factory = new PageElementFactoryImpl(source, 
resolver, null, null, null);
+
+        PageElement element = factory.newEndElement();
+
+        writer.element("root");
+        writer.write("before");
+        writer.element("nested");
+
+        element.render(writer, queue);
+
+        writer.write("after");
+
+        verify();
+
+        assertEquals(element.toString(), "End");
+        assertEquals(writer.toString(), "<root>before<nested/>after</root>");
+    }
+
+    @Test
+    public void end_element_is_singleton()
+    {
+        ComponentInstantiatorSource source = mockComponentInstantiatorSource();
+        ComponentClassResolver resolver = mockComponentClassResolver();
+
+        replay();
+
+        PageElementFactory factory = new PageElementFactoryImpl(source, 
resolver, null, null, null);
+
+        PageElement element1 = factory.newEndElement();
+        PageElement element2 = factory.newEndElement();
+
+        assertSame(element2, element1);
+
+        verify();
+    }
+
+    @Test
+    public void text_element()
+    {
+        ComponentInstantiatorSource source = mockComponentInstantiatorSource();
+        ComponentClassResolver resolver = mockComponentClassResolver();
+        MarkupWriter writer = new MarkupWriterImpl();
+        Location l = mockLocation();
+        RenderQueue queue = mockRenderQueue();
+
+        replay();
+
+        PageElementFactory factory = new PageElementFactoryImpl(source, 
resolver, null, null, null);
+        TextToken token = new TextToken("some text", l);
+
+        PageElement element = factory.newTextElement(token);
+
+        writer.element("root");
+        element.render(writer, queue);
+
+        verify();
+
+        assertEquals(element.toString(), "Text[some text]");
+        assertEquals(writer.toString(), "<root>some text</root>");
+    }
+
+    @Test
+    public void render_body_element()
+    {
+        ComponentInstantiatorSource source = mockComponentInstantiatorSource();
+        ComponentClassResolver resolver = mockComponentClassResolver();
+        RenderQueue queue = mockRenderQueue();
+        ComponentPageElement component = mockComponentPageElement();
+        MarkupWriter writer = newMock(MarkupWriter.class);
+
+        component.enqueueBeforeRenderBody(queue);
+
+        replay();
+
+        PageElementFactory factory = new PageElementFactoryImpl(source, 
resolver, null, null, null);
+
+        PageElement element = factory.newRenderBodyElement(component);
+
+        element.render(writer, queue);
+
+        verify();
+    }
+
+    @Test
+    public void unclosed_attribute_expression()
+    {
+        ComponentInstantiatorSource source = mockComponentInstantiatorSource();
+        ComponentClassResolver resolver = mockComponentClassResolver();
+        TypeCoercer typeCoercer = mockTypeCoercer();
+        BindingSource bindingSource = mockBindingSource();
+        ComponentMessagesSource messagesSource = 
newMock(ComponentMessagesSource.class);
+        ComponentResources resources = mockComponentResources();
+        Location location = mockLocation();
+
+        AttributeToken token = new AttributeToken("fred", "${flintstone", 
location);
+
+        replay();
+
+        PageElementFactory factory = new PageElementFactoryImpl(source, 
resolver, typeCoercer,
+                bindingSource, messagesSource);
+
+        try
+        {
+            factory.newAttributeElement(resources, token);
+            unreachable();
+        }
+        catch (TapestryException ex)
+        {
+            assertEquals(
+                    ex.getMessage(),
+                    "Attribute expression \'${flintstone\' is missing a 
closing brace.");
+            assertSame(ex.getLocation(), location);
+        }
+
+        verify();
+    }
+}

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PageLoaderImplTest.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PageLoaderImplTest.java?view=diff&rev=540188&r1=540187&r2=540188
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PageLoaderImplTest.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PageLoaderImplTest.java
 Mon May 21 09:14:03 2007
@@ -28,7 +28,6 @@
 import org.apache.tapestry.ioc.Location;
 import org.apache.tapestry.model.ComponentModel;
 import org.apache.tapestry.model.EmbeddedComponentModel;
-import org.apache.tapestry.services.BindingSource;
 import org.testng.annotations.Test;
 
 public class PageLoaderImplTest extends InternalBaseTestCase
@@ -44,7 +43,6 @@
     {
         ComponentTemplateSource templateSource = mockComponentTemplateSource();
         PageElementFactory elementFactory = mockPageElementFactory();
-        BindingSource bindingSource = mockBindingSource();
         ComponentPageElement rootElement = mockComponentPageElement();
         InternalComponentResources resources = 
mockInternalComponentResources();
         ComponentModel model = mockComponentModel();
@@ -76,8 +74,7 @@
 
         replay();
 
-        PageLoader loader = new PageLoaderImpl(templateSource, elementFactory, 
bindingSource, null,
-                null);
+        PageLoader loader = new PageLoaderImpl(templateSource, elementFactory, 
null, null);
 
         loader.loadPage(PAGE_CLASS_NAME, LOCALE);
 
@@ -89,7 +86,6 @@
     {
         ComponentTemplateSource templateSource = mockComponentTemplateSource();
         PageElementFactory elementFactory = mockPageElementFactory();
-        BindingSource bindingSource = mockBindingSource();
         ComponentPageElement rootElement = mockComponentPageElement();
         InternalComponentResources resources = 
mockInternalComponentResources();
         ComponentModel model = mockComponentModel();
@@ -163,8 +159,7 @@
 
         replay();
 
-        PageLoader loader = new PageLoaderImpl(templateSource, elementFactory, 
bindingSource, null,
-                null);
+        PageLoader loader = new PageLoaderImpl(templateSource, elementFactory, 
null, null);
 
         loader.loadPage(PAGE_CLASS_NAME, LOCALE);
 

Added: 
tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app1/pages/AttributeExpansionsDemo.properties
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app1/pages/AttributeExpansionsDemo.properties?view=auto&rev=540188
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app1/pages/AttributeExpansionsDemo.properties
 (added)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry/integration/app1/pages/AttributeExpansionsDemo.properties
 Mon May 21 09:14:03 2007
@@ -0,0 +1,16 @@
+# 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.
+
+cssClassPrefix=goober-
+alert=expansions work inside formal component parameters as well
\ No newline at end of file


Reply via email to