You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by jk...@apache.org on 2007/04/21 13:56:23 UTC

svn commit: r531019 - in /tapestry/tapestry4/trunk: tapestry-contrib/src/descriptor/META-INF/ tapestry-contrib/src/java/org/apache/tapestry/contrib/services/ tapestry-contrib/src/java/org/apache/tapestry/contrib/services/impl/ tapestry-contrib/src/test...

Author: jkuhnert
Date: Sat Apr 21 04:56:21 2007
New Revision: 531019

URL: http://svn.apache.org/viewvc?view=rev&rev=531019
Log:
Added a new rounded corners image service and used it in TimeTracker demo.

Added:
    tapestry/tapestry4/trunk/tapestry-contrib/src/java/org/apache/tapestry/contrib/services/
    tapestry/tapestry4/trunk/tapestry-contrib/src/java/org/apache/tapestry/contrib/services/impl/
    tapestry/tapestry4/trunk/tapestry-contrib/src/java/org/apache/tapestry/contrib/services/impl/RoundedCornerGenerator.java
    tapestry/tapestry4/trunk/tapestry-contrib/src/java/org/apache/tapestry/contrib/services/impl/RoundedCornerService.java
    tapestry/tapestry4/trunk/tapestry-contrib/src/test/org/apache/tapestry/contrib/services/
    tapestry/tapestry4/trunk/tapestry-contrib/src/test/org/apache/tapestry/contrib/services/TestRoundedCornerService.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/engine/encoders/PathEncoder.java
    tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/engine/encoders/PathEncoderTest.java
Modified:
    tapestry/tapestry4/trunk/tapestry-contrib/src/descriptor/META-INF/hivemodule.xml
    tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/context/LocaleList.html
    tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/context/WEB-INF/hivemodule.xml
    tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/context/WEB-INF/web.xml
    tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/java/org/apache/tapestry/timetracker/page/LocaleList.java
    tapestry/tapestry4/trunk/tapestry-framework/src/descriptor/META-INF/tapestry.url.xml
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/asset/AssetService.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/engine/ServiceEncoder.java

Modified: tapestry/tapestry4/trunk/tapestry-contrib/src/descriptor/META-INF/hivemodule.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-contrib/src/descriptor/META-INF/hivemodule.xml?view=diff&rev=531019&r1=531018&r2=531019
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-contrib/src/descriptor/META-INF/hivemodule.xml (original)
+++ tapestry/tapestry4/trunk/tapestry-contrib/src/descriptor/META-INF/hivemodule.xml Sat Apr 21 04:56:21 2007
@@ -16,48 +16,62 @@
 -->
 
 <module id="tapestry.contrib" version="4.0.0">
-  
-  Support for the components and pages of the Tapestry contributions library.
-  
-  
-  <service-point id="TableColumnModelSource" interface="org.apache.tapestry.contrib.table.components.TableColumnModelSource">
-    
-    Generates table column models from a string description.
-    
-    <invoke-factory>
-      <construct class="org.apache.tapestry.contrib.table.components.TableColumnModelSourceImpl">
-        <set-service property="expressionEvaluator" service-id="tapestry.ognl.ExpressionEvaluator"/>
-      </construct>
-    </invoke-factory>
-    
-  </service-point>
-
-    
-  <service-point id="TableColumnSource" interface="org.apache.tapestry.contrib.table.model.IAdvancedTableColumnSource">
-    
-    Generates a table column object given several parameters.
-    
-    <invoke-factory>
-      <construct class="org.apache.tapestry.contrib.table.components.DefaultTableColumnSource">
-        <set-service property="expressionEvaluator" service-id="tapestry.ognl.ExpressionEvaluator"/>
-      </construct>
-    </invoke-factory>
-    
-  </service-point>
-
-  
-  <contribution configuration-id="tapestry.services.ApplicationServices">
-    <service name="xtile" object="service:XTileService"/>
-  </contribution>
-  
-  <service-point id="XTileService" interface="org.apache.tapestry.engine.IEngineService">
-    <invoke-factory>
-      <construct class="org.apache.tapestry.contrib.ajax.XTileService">
-        <set-object property="exceptionReporter" value="infrastructure:requestExceptionReporter"/>
-        <set-object property="response" value="infrastructure:response"/>
-        <set-object property="linkFactory" value="infrastructure:linkFactory"/>
-      </construct>
-    </invoke-factory>
-  </service-point>  
-    
-</module>
\ No newline at end of file
+
+    Support for the components and pages of the Tapestry contributions library.
+
+
+    <service-point id="TableColumnModelSource" interface="org.apache.tapestry.contrib.table.components.TableColumnModelSource">
+
+        Generates table column models from a string description.
+
+        <invoke-factory>
+            <construct class="org.apache.tapestry.contrib.table.components.TableColumnModelSourceImpl">
+                <set-service property="expressionEvaluator" service-id="tapestry.ognl.ExpressionEvaluator"/>
+            </construct>
+        </invoke-factory>
+
+    </service-point>
+
+
+    <service-point id="TableColumnSource" interface="org.apache.tapestry.contrib.table.model.IAdvancedTableColumnSource">
+
+        Generates a table column object given several parameters.
+
+        <invoke-factory>
+            <construct class="org.apache.tapestry.contrib.table.components.DefaultTableColumnSource">
+                <set-service property="expressionEvaluator" service-id="tapestry.ognl.ExpressionEvaluator"/>
+            </construct>
+        </invoke-factory>
+
+    </service-point>
+
+
+    <contribution configuration-id="tapestry.services.ApplicationServices">
+        <service name="xtile" object="service:XTileService"/>
+    </contribution>
+
+    <service-point id="XTileService" interface="org.apache.tapestry.engine.IEngineService">
+        <invoke-factory>
+            <construct class="org.apache.tapestry.contrib.ajax.XTileService">
+                <set-object property="exceptionReporter" value="infrastructure:requestExceptionReporter"/>
+                <set-object property="response" value="infrastructure:response"/>
+                <set-object property="linkFactory" value="infrastructure:linkFactory"/>
+            </construct>
+        </invoke-factory>
+    </service-point>
+
+    <service-point id="RoundedCornerService" interface="org.apache.tapestry.engine.IEngineService">
+        <invoke-factory>
+            <construct class="org.apache.tapestry.contrib.services.impl.RoundedCornerService" >
+                <set-object property="exceptionReporter" value="infrastructure:requestExceptionReporter"/>
+                <set-object property="response" value="infrastructure:response"/>
+                <set-object property="linkFactory" value="infrastructure:linkFactory"/>
+            </construct>
+        </invoke-factory>
+    </service-point>
+
+    <contribution configuration-id="tapestry.services.ApplicationServices">
+        <service name="rounded" object="service:RoundedCornerService"/>
+    </contribution>
+
+</module>

Added: tapestry/tapestry4/trunk/tapestry-contrib/src/java/org/apache/tapestry/contrib/services/impl/RoundedCornerGenerator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-contrib/src/java/org/apache/tapestry/contrib/services/impl/RoundedCornerGenerator.java?view=auto&rev=531019
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-contrib/src/java/org/apache/tapestry/contrib/services/impl/RoundedCornerGenerator.java (added)
+++ tapestry/tapestry4/trunk/tapestry-contrib/src/java/org/apache/tapestry/contrib/services/impl/RoundedCornerGenerator.java Sat Apr 21 04:56:21 2007
@@ -0,0 +1,143 @@
+package org.apache.tapestry.contrib.services.impl;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.geom.Arc2D;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Class responsible for bulk of java2d manipulation work when used in the {@link RoundedCornerService}. 
+ */
+public class RoundedCornerGenerator {
+
+    // css2 color spec - http://www.w3.org/TR/REC-CSS2/syndata.html#color-units
+    private static final Map _cssSpecMap = new HashMap();
+
+    static {
+        _cssSpecMap.put("aqua", new Color(0,255,255));
+        _cssSpecMap.put("black", Color.black);
+        _cssSpecMap.put("blue", Color.blue);
+        _cssSpecMap.put("fuchsia", new Color(255,0,255));
+        _cssSpecMap.put("gray", Color.gray);
+        _cssSpecMap.put("green", Color.green);
+        _cssSpecMap.put("lime", new Color(0,255,0));
+        _cssSpecMap.put("maroon", new Color(128,0,0));
+        _cssSpecMap.put("navy", new Color(0,0,128));
+        _cssSpecMap.put("olive", new Color(128,128,0));
+        _cssSpecMap.put("purple", new Color(128,0,128));
+        _cssSpecMap.put("red", Color.red);
+        _cssSpecMap.put("silver", new Color(192,192,192));
+        _cssSpecMap.put("teal", new Color(0,128,128));
+        _cssSpecMap.put("white", Color.white);
+        _cssSpecMap.put("yellow", Color.yellow);
+    }
+
+    public static final String TOP_LEFT = "tl";
+    public static final String TOP_RIGHT = "tr";
+    public static final String BOTTOM_LEFT = "bl";
+    public static final String BOTTOM_RIGHT = "br";
+
+    // holds pre-built binaries for previously generated colors
+    private Map _imageCache = new HashMap();
+
+    public byte[] buildCorner(String color, String backgroundColor, int width, int height, String angle)
+    throws Exception
+    {
+        String hashKey = color + backgroundColor + width + height + angle;
+
+        byte[] ret = (byte[])_imageCache.get(hashKey);
+        if (ret != null)
+            return ret;
+        
+        Color arcColor = decodeColor(color);
+        Color bgColor = backgroundColor == null ? null : decodeColor(backgroundColor);
+        float startAngle = getStartAngle(angle);
+
+        BufferedImage img = new BufferedImage( width, height, BufferedImage.TYPE_INT_ARGB);
+        Graphics2D g2 = (Graphics2D) img.createGraphics();
+
+        Arc2D.Float fillArea = new Arc2D.Float(0f, 0f, (float)width, (float)height, startAngle, 90, Arc2D.PIE);
+        
+        // fill background color
+
+        if (bgColor != null) {
+            
+            g2.setClip(fillArea.getBounds2D());
+            g2.setColor(bgColor);
+            g2.fillRect(0, 0, width, height);
+        }
+
+        // draw arc
+        
+        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        
+        g2.setColor(arcColor);
+        g2.setComposite(AlphaComposite.Src);
+        g2.fill(fillArea);
+        
+        g2.dispose();
+
+        ByteArrayOutputStream bo = null;
+
+        try {
+            ImageIO.setUseCache(false);
+
+            bo = new ByteArrayOutputStream();
+
+            ImageIO.write(img, "gif", bo);
+
+            ret = bo.toByteArray();
+
+            _imageCache.put(hashKey, ret);
+
+            return ret;
+
+        } finally {
+            if (bo != null) {
+                bo.close();
+            }
+        }
+    }
+
+    /**
+     * Matches the incoming string against one of the constants defined; tl, tr, bl, br.
+     *
+     * @param code The code for the angle of the arc to generate, if no match is found the default is
+     *          {@link #TOP_RIGHT} - or 0 degrees.
+     * @return The pre-defined 90 degree angle starting degree point.
+     */
+    public float getStartAngle(String code)
+    {
+        if (TOP_LEFT.equalsIgnoreCase(code))
+            return 90f;
+        if (TOP_RIGHT.equalsIgnoreCase(code))
+            return 0f;
+        if (BOTTOM_LEFT.equalsIgnoreCase(code))
+            return 180f;
+        if (BOTTOM_RIGHT.equalsIgnoreCase(code))
+            return 270f;
+
+        return 0f;
+    }
+
+    /**
+     * Decodes the specified input color string into a compatible awt color object. Valid inputs
+     * are any in the css2 color spec or hex strings.
+     * 
+     * @param color The color to match.
+     * @return The decoded color object, may be black if decoding fails.
+     */
+    public Color decodeColor(String color)
+    {
+        Color specColor = (Color) _cssSpecMap.get(color);
+        if (specColor != null)
+            return specColor;
+
+        String hexColor = color.startsWith("0x") ? color : "0x" + color;
+        
+        return Color.decode(hexColor);
+    }
+}

Added: tapestry/tapestry4/trunk/tapestry-contrib/src/java/org/apache/tapestry/contrib/services/impl/RoundedCornerService.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-contrib/src/java/org/apache/tapestry/contrib/services/impl/RoundedCornerService.java?view=auto&rev=531019
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-contrib/src/java/org/apache/tapestry/contrib/services/impl/RoundedCornerService.java (added)
+++ tapestry/tapestry4/trunk/tapestry-contrib/src/java/org/apache/tapestry/contrib/services/impl/RoundedCornerService.java Sat Apr 21 04:56:21 2007
@@ -0,0 +1,128 @@
+package org.apache.tapestry.contrib.services.impl;
+
+import org.apache.hivemind.util.Defense;
+import org.apache.tapestry.IRequestCycle;
+import org.apache.tapestry.engine.IEngineService;
+import org.apache.tapestry.engine.ILink;
+import org.apache.tapestry.error.RequestExceptionReporter;
+import org.apache.tapestry.services.LinkFactory;
+import org.apache.tapestry.services.ServiceConstants;
+import org.apache.tapestry.util.ContentType;
+import org.apache.tapestry.web.WebResponse;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Provides generated rounded corner images in a similar use / fashion as
+ * outlined here: <a href="http://xach.livejournal.com/95656.html">google's own cornershop</a>.
+ */
+public class RoundedCornerService implements IEngineService {
+
+    public static final String SERVICE_NAME = "rounded";
+
+    public static final String PARM_COLOR = "c";
+    public static final String PARM_BACKGROUND_COLOR = "bc";
+    public static final String PARM_WIDTH = "w";
+    public static final String PARM_HEIGHT = "h";
+    public static final String PARM_ANGLE = "a";
+
+    private static final long MONTH_SECONDS = 60 * 60 * 24 * 30;
+
+    private static final long EXPIRES = System.currentTimeMillis() + 365 * 24 * 60 * 60 * 1000L;
+
+    private RequestExceptionReporter _exceptionReporter;
+
+    private LinkFactory _linkFactory;
+
+    private WebResponse _response;
+
+    private RoundedCornerGenerator _generator = new RoundedCornerGenerator();
+    
+    public ILink getLink(boolean post, Object parameter)
+    {
+        Defense.notNull(parameter, "parameter");
+        Defense.isAssignable(parameter, Object[].class, "parameter");
+        
+        Object[] parms = (Object[]) parameter;
+        
+        Map parameters = new HashMap();
+        parameters.put(ServiceConstants.SERVICE, getName());
+        parameters.put(ServiceConstants.PARAMETER, parms);
+        
+        return _linkFactory.constructLink(this, post, parameters, false);
+    }
+
+    public void service(IRequestCycle cycle)
+            throws IOException
+    {
+        String color = cycle.getParameter(PARM_COLOR);
+        String bgColor = cycle.getParameter(PARM_BACKGROUND_COLOR);
+        int width = getIntParam(cycle.getParameter(PARM_WIDTH));
+        int height = getIntParam(cycle.getParameter(PARM_HEIGHT));
+        String angle = cycle.getParameter(PARM_ANGLE);
+
+        OutputStream os = null;
+
+        try {
+
+            byte[] data = _generator.buildCorner(color, bgColor, width, height, angle);
+
+            _response.setDateHeader("Expires", EXPIRES);
+            _response.setHeader("Cache-Control", "public, max-age=" + (MONTH_SECONDS * 3));
+            _response.setContentLength(data.length);
+            
+            os = _response.getOutputStream(new ContentType("image/gif"));
+
+            os.write(data);
+            
+        } catch (Throwable ex) {
+
+            ex.printStackTrace();
+            _exceptionReporter.reportRequestException("Error creating image.", ex);
+        } finally {
+            try {
+                if (os != null) {
+                    os.flush();
+                    os.close();
+                }
+            } catch (Throwable t) {
+                // ignore
+            }
+
+        }
+    }
+
+    private int getIntParam(String value)
+    {
+        if (value == null)
+            return 0;
+        
+        return Integer.valueOf(value).intValue();
+    }
+
+    public String getName()
+    {
+        return SERVICE_NAME;
+    }
+
+    /* Injected */
+    public void setExceptionReporter(RequestExceptionReporter exceptionReporter)
+    {
+        _exceptionReporter = exceptionReporter;
+    }
+
+    /* Injected */
+    public void setLinkFactory(LinkFactory linkFactory)
+    {
+        _linkFactory = linkFactory;
+    }
+
+    /* Injected */
+    public void setResponse(WebResponse response)
+    {
+        _response = response;
+    }
+}

Added: tapestry/tapestry4/trunk/tapestry-contrib/src/test/org/apache/tapestry/contrib/services/TestRoundedCornerService.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-contrib/src/test/org/apache/tapestry/contrib/services/TestRoundedCornerService.java?view=auto&rev=531019
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-contrib/src/test/org/apache/tapestry/contrib/services/TestRoundedCornerService.java (added)
+++ tapestry/tapestry4/trunk/tapestry-contrib/src/test/org/apache/tapestry/contrib/services/TestRoundedCornerService.java Sat Apr 21 04:56:21 2007
@@ -0,0 +1,34 @@
+package org.apache.tapestry.contrib.services;
+
+import com.javaforge.tapestry.testng.TestBase;
+import org.apache.tapestry.contrib.services.impl.RoundedCornerGenerator;
+import org.testng.annotations.Test;
+
+/**
+ * Tests functionality of {@link org.apache.tapestry.contrib.services.impl.RoundedCornerService}.
+ */
+@Test
+public class TestRoundedCornerService extends TestBase {
+
+    public void test_Build_Corner()
+    throws Exception
+    {
+        RoundedCornerGenerator generator = new RoundedCornerGenerator();
+
+        byte[] data = generator.buildCorner("FF7E00", null, 30, 30, "tr");
+
+        assert data != null;
+        assert data.length > 0;        
+    }
+
+    public void test_Corner_Cached()
+    throws Exception
+    {
+        RoundedCornerGenerator generator = new RoundedCornerGenerator();
+
+        byte[] data1 = generator.buildCorner("FF7E00", null, 30, 30, "tr");
+        byte[] data2 = generator.buildCorner("FF7E00", null, 30, 30, "tr");
+
+        assert data1 == data2;
+    }
+}

Modified: tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/context/LocaleList.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/context/LocaleList.html?view=diff&rev=531019&r1=531018&r2=531019
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/context/LocaleList.html (original)
+++ tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/context/LocaleList.html Sat Apr 21 04:56:21 2007
@@ -6,19 +6,41 @@
 
 <style>
 	.localeList {
-		-moz-border-radius: 8px;
-		background: #2A78B0;
-		display:block;
+		/* padding:0.3em; */
+        background: #2A78B0;
+        display:block;
 		float:left;
 		width:20%;
-		margin-top:0.6em;
+        margin-top:0.6em;
 		margin-left:0.5em;
-		padding:0.3em;
-	}
-	
-	.localeList a { color: #fff;}
-	
-	.selected {
+    }
+
+    .localeList a { color: #fff;}
+
+    .localeList p { margin: 0 10px;}
+
+    .roundtop {
+        background: url("/rounded?c=2A78B0&bc=white&w=8&h=8&a=tr") no-repeat top right;
+    }
+    .selectedRoundtop {
+        background: url("/rounded?c=efefef&bc=white&w=8&h=8&a=tr") no-repeat top right;
+    }
+
+    .roundbottom {
+        background: url("/rounded?c=2A78B0&bc=white&w=8&h=8&a=br") no-repeat top right;
+    }
+    .selectedRoundbottom {
+        background: url("/rounded?c=efefef&bc=white&w=8&h=8&a=br") no-repeat top right;
+    }
+
+    img.corner {
+        width: 8px;
+        height: 8px;
+        border: none;
+        display: block !important;
+    }
+
+    .selected {
 		background: #efefef;
 	}
 	
@@ -45,12 +67,11 @@
 		width:70%;
 		height: 50px;
 		overflow:auto;
-		font-family:Arial;
+		font-family: arial;
 		font-size:8pt;
 		padding: 0.4em;
 		border:1px dotted #D6AE33;
 	}
-	
 </style>
 
 <span jwcid="localeDetail@Any">
@@ -70,14 +91,26 @@
 	<span jwcid="@Insert" value="ognl:status" />
 </div>
 
-<div class="ognl:(selected != null and currLocale.toString() == selected.toString()) ? 'selected localeList' : 'localeList'" 
-		jwcid="localeList@For" 
-		source="ognl:@org.apache.tapestry.timetracker.page.LocaleList@LOCALES" value="ognl:currLocale">
-	
-	<a jwcid="localeLink@DirectLink" listener="listener:selectLocale" parameters="ognl:{currLocale.language, currLocale.country, currLocale.variant}"
-		updateComponents="ognl:{'localeDetail',page.components.localeList.clientId}">
-		<span jwcid="@Insert" value="ognl:currLocale.toString()" />
-	</a>
+<div class="ognl:currentSelected ? 'selected localeList' : 'localeList'"
+     jwcid="localeList@For"
+     source="ognl:@org.apache.tapestry.timetracker.page.LocaleList@LOCALES" value="ognl:currLocale">
+
+    <div jwcid="@Any" class="ognl:currentSelected ? 'selectedRoundtop' : 'roundtop'">
+        
+        <img jwcid="@Any" src="ognl:getRoundedUrl('tl')" width="8" height="8" class="corner" style="display: none" />
+        
+    </div>
+    <p>
+    <a jwcid="localeLink@DirectLink" listener="listener:selectLocale" parameters="ognl:{currLocale.language, currLocale.country, currLocale.variant}"
+       updateComponents="ognl:{'localeDetail',page.components.localeList.clientId}">
+        <span jwcid="@Insert" value="ognl:currLocale.toString()" />
+    </a>
+    </p>
+    <div jwcid="@Any" class="ognl:currentSelected ? 'selectedRoundbottom' : 'roundbottom'">
+
+        <img jwcid="@Any" src="ognl:getRoundedUrl('bl')" width="8" height="8" class="corner" style="display: none" />
+
+    </div>
 </div>
 <br/>
 

Modified: tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/context/WEB-INF/hivemodule.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/context/WEB-INF/hivemodule.xml?view=diff&rev=531019&r1=531018&r2=531019
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/context/WEB-INF/hivemodule.xml (original)
+++ tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/context/WEB-INF/hivemodule.xml Sat Apr 21 04:56:21 2007
@@ -22,6 +22,7 @@
         <extension-encoder id="extension" extension="svc" after="*"/>
         <direct-service-encoder id="direct" stateless-extension="direct" stateful-extension="sdirect"/>
         <page-service-encoder id="page" extension="html" service="page"/>
+        <path-encoder id="rounded" path="/rounded" service="rounded" />
     </contribution>
     
     <sub-module descriptor="timetracker.db.xml" />

Modified: tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/context/WEB-INF/web.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/context/WEB-INF/web.xml?view=diff&rev=531019&r1=531018&r2=531019
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/context/WEB-INF/web.xml (original)
+++ tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/context/WEB-INF/web.xml Sat Apr 21 04:56:21 2007
@@ -16,68 +16,73 @@
 -->
 
 <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
- "http://java.sun.com/dtd/web-app_2_3.dtd">
+        "http://java.sun.com/dtd/web-app_2_3.dtd">
 <web-app>
-  <display-name>Tapestry Time Tracking example application</display-name>
-  
-	<filter>
-		<filter-name>redirect</filter-name>
-		<filter-class>org.apache.tapestry.RedirectFilter</filter-class>
-	</filter>
-	
-	<filter-mapping>
-		<filter-name>redirect</filter-name>
-		<url-pattern>/</url-pattern>
-	</filter-mapping>
-  
-  <servlet>
-  	<servlet-name>timetracker</servlet-name>
-    <servlet-class>org.apache.tapestry.timetracker.servlet.ConfigurationServlet</servlet-class>
-  	<load-on-startup>0</load-on-startup>
-  </servlet>
-     
-  <servlet-mapping>
-  	<servlet-name>timetracker</servlet-name>
-  	<url-pattern>/app</url-pattern>
-  </servlet-mapping>
-  
-  <servlet-mapping>
-    <servlet-name>timetracker</servlet-name>
-    <url-pattern>*.page</url-pattern>
-  </servlet-mapping>
-  
-  <servlet-mapping>
-    <servlet-name>timetracker</servlet-name>
-    <url-pattern>*.external</url-pattern>
-  </servlet-mapping>
-  
-  <servlet-mapping>
-    <servlet-name>timetracker</servlet-name>
-    <url-pattern>*.direct</url-pattern>
-  </servlet-mapping>
-  
-  <servlet-mapping>
-    <servlet-name>timetracker</servlet-name>
-    <url-pattern>*.sdirect</url-pattern>
-  </servlet-mapping>  
-  
-  <servlet-mapping>
-    <servlet-name>timetracker</servlet-name>
-    <url-pattern>*.svc</url-pattern>
-  </servlet-mapping>
-  
-  <servlet-mapping>
-    <servlet-name>timetracker</servlet-name>
-    <url-pattern>/assets/*</url-pattern>
-  </servlet-mapping>
-  
-  <servlet-mapping>
-    <servlet-name>timetracker</servlet-name>
-    <url-pattern>*.html</url-pattern>
-  </servlet-mapping>
-  
-  <session-config>
-  	<session-timeout>15</session-timeout>
-  </session-config>
-  
+    <display-name>Tapestry Time Tracking example application</display-name>
+
+    <filter>
+        <filter-name>redirect</filter-name>
+        <filter-class>org.apache.tapestry.RedirectFilter</filter-class>
+    </filter>
+
+    <filter-mapping>
+        <filter-name>redirect</filter-name>
+        <url-pattern>/</url-pattern>
+    </filter-mapping>
+
+    <servlet>
+        <servlet-name>timetracker</servlet-name>
+        <servlet-class>org.apache.tapestry.timetracker.servlet.ConfigurationServlet</servlet-class>
+        <load-on-startup>0</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>timetracker</servlet-name>
+        <url-pattern>/app</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>timetracker</servlet-name>
+        <url-pattern>*.page</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>timetracker</servlet-name>
+        <url-pattern>*.external</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>timetracker</servlet-name>
+        <url-pattern>*.direct</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>timetracker</servlet-name>
+        <url-pattern>*.sdirect</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>timetracker</servlet-name>
+        <url-pattern>*.svc</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>timetracker</servlet-name>
+        <url-pattern>/assets/*</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>timetracker</servlet-name>
+        <url-pattern>*.html</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>timetracker</servlet-name>
+        <url-pattern>/rounded</url-pattern>
+    </servlet-mapping>
+
+    <session-config>
+        <session-timeout>15</session-timeout>
+    </session-config>
+
 </web-app>

Modified: tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/java/org/apache/tapestry/timetracker/page/LocaleList.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/java/org/apache/tapestry/timetracker/page/LocaleList.java?view=diff&rev=531019&r1=531018&r2=531019
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/java/org/apache/tapestry/timetracker/page/LocaleList.java (original)
+++ tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/java/org/apache/tapestry/timetracker/page/LocaleList.java Sat Apr 21 04:56:21 2007
@@ -32,6 +32,7 @@
     public abstract Locale getCurrLocale();
     
     public abstract void setSelected(Locale locale);
+    public abstract Locale getSelected();
     
     public abstract void setStatus(String status);
     
@@ -42,5 +43,17 @@
         setSelected(new Locale(language, country, variant));
         setStatus(event.toString());
         getBuilder().updateComponent("status");
+    }
+
+    public boolean isCurrentSelected()
+    {
+        return getSelected() != null && getCurrLocale().toString().equals(getSelected().toString());
+    }
+
+    public String getRoundedUrl(String anchor)
+    {
+        return "/rounded?c=" +
+               (isCurrentSelected() ? "efefef" : "2A78B0")
+               + "&bc=white&w=8&h=8&a=" + anchor;
     }
 }

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/descriptor/META-INF/tapestry.url.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/descriptor/META-INF/tapestry.url.xml?view=diff&rev=531019&r1=531018&r2=531019
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/descriptor/META-INF/tapestry.url.xml (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/descriptor/META-INF/tapestry.url.xml Sat Apr 21 04:56:21 2007
@@ -205,7 +205,40 @@
         </rules>        
         
       </element>
-      
+
+      <element name="path-encoder">
+
+          An encoder the encodes just the service name as a path such as "/service" .
+        <attribute name="id" required="true" unique="true">
+          Unique id for this encoder, used when ordering encoder order.
+        </attribute>
+        <attribute name="before">
+          List of ids of encoders that must come before this encoder.
+        </attribute>
+        <attribute name="after">
+          List of ids of encoders that must follow this encoder.
+        </attribute>
+        <attribute name="path" required="true">
+          The path to map to the service, typically "/service".
+        </attribute>
+        <attribute name="service" required="true">
+            The service name that can be used to find the right service to handle the path.
+        </attribute>
+        <rules>
+          <create-object class="impl.ServiceEncoderContribution"/>
+          <read-attribute attribute="id" property="id"/>
+          <read-attribute attribute="before" property="before"/>
+          <read-attribute attribute="after" property="after"/>
+          <invoke-parent method="addElement"/>
+
+          <create-object class="org.apache.tapestry.engine.encoders.PathEncoder"/>
+          <read-attribute attribute="path" property="path"/>
+          <read-attribute attribute="service" property="service" />
+          <invoke-parent method="setEncoder"/>
+        </rules>
+
+      </element>
+
     </schema>
     
   </configuration-point>

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/asset/AssetService.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/asset/AssetService.java?view=diff&rev=531019&r1=531018&r2=531019
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/asset/AssetService.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/asset/AssetService.java Sat Apr 21 04:56:21 2007
@@ -14,21 +14,6 @@
 
 package org.apache.tapestry.asset;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.URL;
-import java.net.URLConnection;
-import java.text.DateFormat;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.zip.GZIPOutputStream;
-
-import javax.servlet.http.HttpServletResponse;
-
 import org.apache.commons.io.FilenameUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.logging.Log;
@@ -49,6 +34,20 @@
 import org.apache.tapestry.web.WebRequest;
 import org.apache.tapestry.web.WebResponse;
 
+import javax.servlet.http.HttpServletResponse;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.text.DateFormat;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.zip.GZIPOutputStream;
+
 /**
  * A service for building URLs to and accessing {@link org.apache.tapestry.IAsset}s. Most of the
  * work is deferred to the {@link org.apache.tapestry.IAsset}instance.
@@ -220,7 +219,7 @@
         String md5Digest = cycle.getParameter(DIGEST);
         boolean checkDigest = !_unprotectedMatcher.containsResource(path);
         
-        URLConnection resourceConnection = null;
+        URLConnection resourceConnection;
         
         try
         {
@@ -261,10 +260,7 @@
         catch (Throwable ex)
         {
             _exceptionReporter.reportRequestException(AssetMessages.exceptionReportTitle(path), ex);
-        } finally {
-            resourceConnection = null;
         }
-
     }
 
     /**
@@ -391,7 +387,7 @@
 
         try {
             
-            CachedAsset cache = null;
+            CachedAsset cache;
             byte[] data = null;
             
             // check cache first
@@ -447,7 +443,6 @@
             
             if (input != null) {
                 IOUtils.closeQuietly(input);
-                input = null;
             }
         }
     }

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/engine/ServiceEncoder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/engine/ServiceEncoder.java?view=diff&rev=531019&r1=531018&r2=531019
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/engine/ServiceEncoder.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/engine/ServiceEncoder.java Sat Apr 21 04:56:21 2007
@@ -17,7 +17,6 @@
 /**
  * Encapsulates the logic for encoding and decoding service requests.
  * 
- * @author Howard M. Lewis Ship
  * @since 4.0
  */
 public interface ServiceEncoder

Added: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/engine/encoders/PathEncoder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/engine/encoders/PathEncoder.java?view=auto&rev=531019
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/engine/encoders/PathEncoder.java (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/engine/encoders/PathEncoder.java Sat Apr 21 04:56:21 2007
@@ -0,0 +1,42 @@
+package org.apache.tapestry.engine.encoders;
+
+import org.apache.tapestry.engine.ServiceEncoder;
+import org.apache.tapestry.engine.ServiceEncoding;
+import org.apache.tapestry.services.ServiceConstants;
+
+/**
+ * Encoder for mapping service names as simple paths such as "/service" . 
+ */
+public class PathEncoder implements ServiceEncoder {
+
+    private String _path;
+
+    private String _service;
+
+    public void setPath(String path)
+    {
+        _path = path;
+    }
+
+    public void setService(String service)
+    {
+        _service = service;
+    }
+
+    public void encode(ServiceEncoding encoding)
+    {
+        if (!encoding.getParameterValue(ServiceConstants.SERVICE).equals(_service))
+            return;
+           
+        encoding.setServletPath(_path);
+        encoding.setParameterValue(ServiceConstants.SERVICE, null);
+    }
+
+    public void decode(ServiceEncoding encoding)
+    {
+        if (!encoding.getServletPath().equals(_path))
+            return;
+        
+        encoding.setParameterValue(ServiceConstants.SERVICE, _service);
+    }
+}

Added: tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/engine/encoders/PathEncoderTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/engine/encoders/PathEncoderTest.java?view=auto&rev=531019
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/engine/encoders/PathEncoderTest.java (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/engine/encoders/PathEncoderTest.java Sat Apr 21 04:56:21 2007
@@ -0,0 +1,82 @@
+package org.apache.tapestry.engine.encoders;
+
+import com.javaforge.tapestry.testng.TestBase;
+import org.apache.tapestry.engine.ServiceEncoding;
+import org.apache.tapestry.services.ServiceConstants;
+import static org.easymock.EasyMock.expect;
+import org.testng.annotations.Test;
+
+/**
+ * Tests functionality of the {@link PathEncoder} .
+ */
+@Test
+public class PathEncoderTest extends TestBase {
+
+    public void test_Wrong_Service()
+    {
+        ServiceEncoding encoding = newEncoding();
+
+        trainGetParameterValue(encoding, ServiceConstants.SERVICE, "foo");
+
+        replay();
+
+        PathEncoder encoder = new PathEncoder();
+        encoder.setService("bar");
+        
+        encoder.encode(encoding);
+
+        verify();
+    }
+
+    protected void trainGetParameterValue(ServiceEncoding encoding, String name, String value)
+    {
+        expect(encoding.getParameterValue(name)).andReturn(value);
+    }
+
+    protected ServiceEncoding newEncoding()
+    {
+        return newMock(ServiceEncoding.class);
+    }
+
+    public void test_Wrong_Path()
+    {
+        ServiceEncoding encoding = newEncoding();
+
+        trainGetServletPath(encoding, "/Home.page");
+
+        replay();
+
+        PathEncoder encoder = new PathEncoder();
+        encoder.setPath("/rounded");
+        encoder.setService("rounded");
+        
+        encoder.decode(encoding);
+
+        verify();
+    }
+
+    protected void trainGetServletPath(ServiceEncoding encoding, String servletPath)
+    {
+        expect(encoding.getServletPath()).andReturn(servletPath);
+    }
+
+    public void test_Encode()
+    {
+        ServiceEncoding encoding = newEncoding();
+
+        trainGetParameterValue(encoding, ServiceConstants.SERVICE, "rounded");
+
+        encoding.setServletPath("/rounded");
+        encoding.setParameterValue(ServiceConstants.SERVICE, null);
+
+        replay();
+
+        PathEncoder encoder = new PathEncoder();
+        encoder.setPath("/rounded");
+        encoder.setService("rounded");
+
+        encoder.encode(encoding);
+
+        verify();
+    }
+}