You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by iv...@apache.org on 2009/10/11 02:02:29 UTC

svn commit: r823984 [6/11] - in /wicket/trunk: ./ wicket/src/main/java/org/apache/wicket/ng/ wicket/src/main/java/org/apache/wicket/ng/application/ wicket/src/main/java/org/apache/wicket/ng/behavior/ wicket/src/main/java/org/apache/wicket/ng/markup/ wi...

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/PageInstanceMapper.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/PageInstanceMapper.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/PageInstanceMapper.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/PageInstanceMapper.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.wicket.ng.request.mapper;
+
+import org.apache.wicket.ng.request.Request;
+import org.apache.wicket.ng.request.RequestHandler;
+import org.apache.wicket.ng.request.Url;
+import org.apache.wicket.ng.request.component.RequestablePage;
+import org.apache.wicket.ng.request.handler.PageAndComponentProvider;
+import org.apache.wicket.ng.request.handler.PageProvider;
+import org.apache.wicket.ng.request.handler.impl.ListenerInterfaceRequestHandler;
+import org.apache.wicket.ng.request.handler.impl.RenderPageRequestHandler;
+import org.apache.wicket.ng.request.listener.RequestListenerInterface;
+import org.apache.wicket.ng.request.mapper.info.ComponentInfo;
+import org.apache.wicket.ng.request.mapper.info.PageComponentInfo;
+import org.apache.wicket.ng.request.mapper.info.PageInfo;
+
+/**
+ * Decodes and encodes the following URLs:
+ * 
+ * <pre>
+ *  Page Instance - Render (RenderPageRequestHandler)
+ *  /wicket/page?2
+ * 
+ * 
+ *  Page Instance - Listener (ListenerInterfaceRequestHandler)
+ *  /wicket/page?2-click-foo-bar-baz
+ *  /wicket/page?2-click.1-foo-bar-baz (1 is behavior index)
+ * </pre>
+ * 
+ * @author Matej Knopp
+ */
+public class PageInstanceMapper extends AbstractMapper
+{
+
+	/**
+	 * Construct.
+	 */
+	public PageInstanceMapper()
+	{
+	}
+
+	public RequestHandler mapRequest(Request request)
+	{
+		Url url = request.getUrl();
+		if (urlStartsWith(url, getContext().getNamespace(), getContext().getPageIdentifier()))
+		{
+			PageComponentInfo info = getPageComponentInfo(url);
+			if (info != null && info.getPageInfo().getPageId() != null)
+			{
+				Integer renderCount = info.getComponentInfo() != null ? info.getComponentInfo().getRenderCount() : null;
+
+				if (info.getComponentInfo() == null)
+				{
+					PageProvider provider = new PageProvider(info.getPageInfo().getPageId(), renderCount);
+					provider.setPageSource(getContext());
+					// render page					
+					return new RenderPageRequestHandler(provider);
+				}
+				else
+				{
+					ComponentInfo componentInfo = info.getComponentInfo();
+
+					PageAndComponentProvider provider = new PageAndComponentProvider(info.getPageInfo()
+							.getPageId(), renderCount, componentInfo.getComponentPath());
+
+					provider.setPageSource(getContext());
+					
+					// listener interface
+					RequestListenerInterface listenerInterface = requestListenerInterfaceFromString(componentInfo
+							.getListenerInterface());
+
+					return new ListenerInterfaceRequestHandler(provider, listenerInterface, componentInfo
+							.getBehaviorIndex());
+				}
+			}
+		}
+		return null;
+	}
+
+	public Url mapHandler(RequestHandler requestHandler)
+	{
+		PageComponentInfo info = null;
+
+		if (requestHandler instanceof RenderPageRequestHandler)
+		{
+			RequestablePage page = ((RenderPageRequestHandler) requestHandler).getPage();
+
+			PageInfo i = new PageInfo(page);
+			info = new PageComponentInfo(i, null);
+		}
+		else if (requestHandler instanceof ListenerInterfaceRequestHandler)
+		{
+			ListenerInterfaceRequestHandler handler = (ListenerInterfaceRequestHandler) requestHandler;
+			RequestablePage page = handler.getPage();
+			String componentPath = handler.getComponent().getPath();
+			RequestListenerInterface listenerInterface = handler.getListenerInterface();
+
+			PageInfo pageInfo = new PageInfo(page);
+			ComponentInfo componentInfo = new ComponentInfo(page.getRenderCount(),
+					requestListenerInterfaceToString(listenerInterface), componentPath, handler.getBehaviorIndex());
+			info = new PageComponentInfo(pageInfo, componentInfo);
+		}
+
+		if (info != null)
+		{
+			Url url = new Url();
+			url.getSegments().add(getContext().getNamespace());
+			url.getSegments().add(getContext().getPageIdentifier());
+			encodePageComponentInfo(url, info);
+			return url;
+		}
+		else
+		{
+			return null;
+		}
+	}
+
+	public int getCompatibilityScore(Request request)
+	{
+		// always return 0 here so that the mounts have higher priority
+		return 0;
+	}
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/PageSource.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/PageSource.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/PageSource.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/PageSource.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,28 @@
+package org.apache.wicket.ng.request.mapper;
+
+import org.apache.wicket.ng.request.component.PageParameters;
+import org.apache.wicket.ng.request.component.RequestablePage;
+
+public interface PageSource
+{
+	/**
+	 * Returns existing page instance if the page exists.
+	 * 
+	 * @param pageId
+	 * @return page instance or <code>null</code> if the page does not exist.
+	 */
+	public RequestablePage getPageInstance(int pageId);
+
+	/**
+	 * Creates new page instance of page with given class. The page should be marked as create
+	 * bookmarkable, so subsequent calls to {@link RequestablePage#wasCreatedBookmarkable()} must return
+	 * <code>true</code>
+	 * 
+	 * @param pageMapName
+	 * @param pageClass
+	 * @param pageParameters
+	 * @return new page instance
+	 */
+	public RequestablePage newPageInstance(Class<? extends RequestablePage> pageClass, PageParameters pageParameters);
+
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/ResourceReferenceMapper.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/ResourceReferenceMapper.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/ResourceReferenceMapper.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/ResourceReferenceMapper.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.wicket.ng.request.mapper;
+
+import org.apache.wicket.ng.request.Request;
+import org.apache.wicket.ng.request.RequestHandler;
+import org.apache.wicket.ng.request.Url;
+import org.apache.wicket.ng.request.component.PageParameters;
+import org.apache.wicket.ng.request.handler.resource.ResourceReferenceRequestHandler;
+import org.apache.wicket.ng.request.handler.resource.ResourceRequestHandler;
+import org.apache.wicket.ng.request.mapper.parameters.PageParametersEncoder;
+import org.apache.wicket.ng.request.mapper.parameters.SimplePageParametersEncoder;
+import org.apache.wicket.ng.resource.Resource;
+import org.apache.wicket.ng.resource.ResourceReference;
+import org.apache.wicket.ng.util.lang.Classes;
+
+/**
+ * Generic {@link ResourceReference} encoder that encodes and decodes non-mounted
+ * {@link ResourceReference}s.
+ * <p>
+ * Decodes and encodes the following URLs:
+ * 
+ * <pre>
+ *    /wicket/resource/org.apache.wicket.ResourceScope/name
+ *    /wicket/resource/org.apache.wicket.ResourceScope/name?en
+ *    /wicket/resource/org.apache.wicket.ResourceScope/name?-style
+ *    /wicket/resource/org.apache.wicket.ResourceScope/resource/name.xyz?en_EN-style
+ * </pre>
+ * 
+ * @author Matej Knopp
+ */
+public class ResourceReferenceMapper extends AbstractResourceReferenceMapper
+{
+	private final PageParametersEncoder pageParametersEncoder;
+
+	/**
+	 * Construct.
+	 * 
+	 * @param pageParametersEncoder
+	 */
+	public ResourceReferenceMapper(PageParametersEncoder pageParametersEncoder)
+	{
+		this.pageParametersEncoder = pageParametersEncoder;
+	}
+
+	/**
+	 * Construct.
+	 */
+	public ResourceReferenceMapper()
+	{
+		this(new SimplePageParametersEncoder());
+	}
+
+	public RequestHandler mapRequest(Request request)
+	{
+		Url url = request.getUrl();
+		if (url.getSegments().size() >= 4
+				&& urlStartsWith(url, getContext().getNamespace(), getContext().getResourceIdentifier()))
+		{
+			String className = url.getSegments().get(2);
+			StringBuilder name = new StringBuilder();
+			for (int i = 3; i < url.getSegments().size(); ++i)
+			{
+				if (name.length() > 0)
+				{
+					name.append("/");
+				}
+				name.append(url.getSegments().get(i));
+			}
+
+			ResourceReferenceAttributes attributes = getResourceReferenceAttributes(url);
+
+			// extract the PageParameters from URL if there are any
+			PageParameters pageParameters = extractPageParameters(request, url.getSegments().size(),
+					pageParametersEncoder);
+
+			Class<?> scope = resolveClass(className);
+			if (scope != null)
+			{
+				ResourceReference res = getContext().getResourceReferenceRegistry().getResourceReference(scope,
+						name.toString(), attributes.locale, attributes.style, false);
+				if (res != null)
+				{
+					Resource resource = res.getResource();
+					if (resource != null)
+					{
+						ResourceRequestHandler handler = new ResourceRequestHandler(resource, attributes.locale,
+								attributes.style, pageParameters);
+						return handler;
+					}
+				}
+			}
+		}
+		return null;
+	}
+
+	protected Class<?> resolveClass(String name)
+	{
+		return Classes.resolveClass(name);
+	}
+
+	protected String getClassName(Class<?> scope)
+	{
+		return scope.getName();
+	}
+
+	public Url mapHandler(RequestHandler requestHandler)
+	{
+		if (requestHandler instanceof ResourceReferenceRequestHandler)
+		{
+			ResourceReferenceRequestHandler referenceRequestHandler = (ResourceReferenceRequestHandler) requestHandler;
+			ResourceReference reference = referenceRequestHandler.getResourceReference();
+			Url url = new Url();
+			url.getSegments().add(getContext().getNamespace());
+			url.getSegments().add(getContext().getResourceIdentifier());
+			url.getSegments().add(getClassName(reference.getScope()));
+			String nameParts[] = reference.getName().split("/");
+			for (String name : nameParts)
+			{
+				url.getSegments().add(name);
+			}
+			encodeResourceReferenceAttributes(url, reference);
+			PageParameters parameters = referenceRequestHandler.getPageParameters();
+			if (parameters != null)
+			{
+				parameters = new PageParameters(parameters);
+				// need to remove indexed parameters otherwise the URL won't be able to decode
+				parameters.clearIndexedParameters();
+				url = encodePageParameters(url, parameters, pageParametersEncoder);
+			}
+			return url;
+		}
+		return null;
+	}
+
+	public int getCompatibilityScore(Request request)
+	{
+		// always return 0 here so that the mounts have higher priority
+		return 0;
+	}
+
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/StalePageException.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/StalePageException.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/StalePageException.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/StalePageException.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.wicket.ng.request.mapper;
+
+import org.apache.wicket.ng.WicketRuntimeException;
+import org.apache.wicket.ng.request.component.RequestablePage;
+
+/**
+ * Exception invoked when when stale link has been clicked. The page should then be rerendered
+ * with an explanatory error message.
+ * 
+ * @author Matej Knopp
+ */
+public class StalePageException extends WicketRuntimeException
+{
+	private static final long serialVersionUID = 1L;
+
+	private final RequestablePage page;
+
+	/**
+	 * 
+	 * Construct.
+	 * @param page
+	 */
+	public StalePageException(RequestablePage page)
+	{
+		this.page = page;
+	}
+	
+	/**
+	 * 
+	 * @return page instance
+	 */
+	public RequestablePage getPage()
+	{
+		return page;
+	}
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/SystemMapper.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/SystemMapper.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/SystemMapper.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/SystemMapper.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,48 @@
+package org.apache.wicket.ng.request.mapper;
+
+import org.apache.wicket.ng.request.Request;
+import org.apache.wicket.ng.request.RequestHandler;
+import org.apache.wicket.ng.request.RequestMapper;
+import org.apache.wicket.ng.request.Url;
+
+/**
+ * Mapper that encapsulates mappers that are necessary for Wicket to function.
+ * 
+ * @author igor.vaynberg
+ * 
+ */
+public class SystemMapper implements RequestMapper
+{
+    private ThreadsafeCompoundRequestMapper mapper = new ThreadsafeCompoundRequestMapper();
+
+    /**
+     * Constructor
+     */
+    public SystemMapper()
+    {
+        mapper.register(new PageInstanceMapper());
+        mapper.register(new BookmarkableMapper());
+        mapper.register(new ResourceReferenceMapper());
+        mapper.register(new BufferedResponseMapper());
+    }
+
+    /** {@inheritDoc} */
+    public int getCompatibilityScore(Request request)
+    {
+        return mapper.getCompatibilityScore(request);
+    }
+
+    /** {@inheritDoc} */
+    public Url mapHandler(RequestHandler handler)
+    {
+        return mapper.mapHandler(handler);
+    }
+
+    /** {@inheritDoc} */
+    public RequestHandler mapRequest(Request request)
+    {
+        return mapper.mapRequest(request);
+    }
+
+
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/ThreadsafeCompoundRequestMapper.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/ThreadsafeCompoundRequestMapper.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/ThreadsafeCompoundRequestMapper.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/ThreadsafeCompoundRequestMapper.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.wicket.ng.request.mapper;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.wicket.ng.request.CompoundRequestMapper;
+import org.apache.wicket.ng.request.Request;
+import org.apache.wicket.ng.request.RequestHandler;
+import org.apache.wicket.ng.request.RequestMapper;
+import org.apache.wicket.ng.request.Url;
+
+
+
+/**
+ * Thread safe compound {@link RequestMapper}. The mappers are searched depending on their
+ * compatibility score and the orders they were registered. If two or more {@link RequestMapper}s
+ * have the same compatibility score, the last registered mapper has highest priority.
+ * 
+ * @author Matej Knopp
+ */
+public class ThreadsafeCompoundRequestMapper implements CompoundRequestMapper
+{
+    /* (non-Javadoc)
+     * @see org.apache.wicket.request.ICompoundRequestMapper#register(org.apache.wicket.request.IRequestMapper)
+     */
+    public void register(RequestMapper encoder)
+    {
+        mappers.add(0, encoder);
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.wicket.request.ICompoundRequestMapper#unregister(org.apache.wicket.request.IRequestMapper)
+     */
+    public void unregister(RequestMapper encoder)
+    {
+        mappers.remove(encoder);
+    }
+
+    private static class EncoderWithSegmentsCount implements Comparable<EncoderWithSegmentsCount>
+    {
+        private final RequestMapper mapper;
+        private final int compatibilityScore;
+
+        public EncoderWithSegmentsCount(RequestMapper encoder, int compatibilityScore)
+        {
+            this.mapper = encoder;
+            this.compatibilityScore = compatibilityScore;
+        }
+
+        public int compareTo(EncoderWithSegmentsCount o)
+        {
+            return o.compatibilityScore - this.compatibilityScore;
+        }
+
+        public RequestMapper getMapper()
+        {
+            return mapper;
+        }
+    };
+
+    /**
+     * Searches the registered {@link RequestMapper}s to find one that can decode the
+     * {@link Request}. Each registered {@link RequestMapper} is asked to provide the matching
+     * segments count. Then the encoders are asked to decode the request in order depending on the
+     * provided segments count.
+     * <p>
+     * The encoder with highest matching segments count that can decode the request is returned.
+     * 
+     * @param request
+     * @return RequestHandler for the request or <code>null</code> if no encoder for the request is
+     *         found.
+     */
+    public RequestHandler mapRequest(Request request)
+    {
+        List<EncoderWithSegmentsCount> list = new ArrayList<EncoderWithSegmentsCount>(mappers
+                .size());
+
+        for (RequestMapper encoder : mappers)
+        {
+            list.add(new EncoderWithSegmentsCount(encoder, encoder.getCompatibilityScore(request)));
+        }
+
+        Collections.sort(list);
+
+        for (EncoderWithSegmentsCount encoder : list)
+        {
+            RequestHandler handler = encoder.getMapper().mapRequest(request);
+            if (handler != null)
+            {
+                return handler;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Searches the registered {@link RequestMapper}s to find one that can encode the
+     * {@link RequestHandler}. Each registered {@link RequestMapper} is asked to encode the
+     * {@link RequestHandler} until an encoder that can encode the {@link RequestHandler} is found
+     * or no more encoders are left.
+     * <p>
+     * The handlers are searched in reverse order as they have been registered. More recently
+     * registered handlers have bigger priority.
+     * 
+     * @param handler
+     * @return Url for the handler or <code>null</code> if no encoder for the handler is found.
+     */
+    public Url mapHandler(RequestHandler handler)
+    {
+        for (RequestMapper encoder : mappers)
+        {
+            Url url = encoder.mapHandler(handler);
+            if (url != null)
+            {
+                return url;
+            }
+        }
+        return null;
+    }
+
+    private List<RequestMapper> mappers = new CopyOnWriteArrayList<RequestMapper>();
+
+    /**
+     * The scope of the compound mapper is the highest score of the registered mappers.
+     * 
+     * {@inheritDoc}
+     */
+    public int getCompatibilityScore(Request request)
+    {
+        int score = Integer.MIN_VALUE;
+        for (RequestMapper mapper : mappers)
+        {
+            score = Math.max(score, mapper.getCompatibilityScore(request));
+        }
+        return score;
+    }
+
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/info/ComponentInfo.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/info/ComponentInfo.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/info/ComponentInfo.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/info/ComponentInfo.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,245 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.wicket.ng.request.mapper.info;
+
+import org.apache.wicket.ng.util.lang.Check;
+import org.apache.wicket.ng.util.string.Strings;
+
+/**
+ * Encodes listener inteface and component path in form of
+ * &lt;listenerInterface&gt-&lt;componentPath&gt;,
+ * &lt;listenerInterface&gt.&lt;behaviorIndex&gt;-&lt;componentPath&gt; or
+ * &lt;render-count&gt;.&lt;listenerInterface&gt.&lt;behaviorIndex&gt;-&lt;componentPath&gt;
+ * <p>
+ * Component path is escaped (':' characters are replaced by '-')
+ * 
+ * @author Matej Knopp
+ */
+public class ComponentInfo
+{
+	private final String listenerInterface;
+	private final String componentPath;
+	private final Integer behaviorIndex;
+	private final Integer renderCount;
+
+	private static final char BEHAVIOR_INDEX_SEPARATOR = '.';
+	private static final char SEPARATOR = '-';
+
+	/**
+	 * Construct.
+	 * 
+	 * @param renderCount
+	 * @param listenerInterface
+	 * @param componentPath
+	 * @param behaviorIndex
+	 */
+	public ComponentInfo(Integer renderCount, String listenerInterface, String componentPath,
+		Integer behaviorIndex)
+	{
+		Check.argumentNotEmpty(listenerInterface, "listenerInterface");
+		Check.argumentNotEmpty(componentPath, "componentPath");
+
+		this.listenerInterface = listenerInterface;
+		this.componentPath = componentPath;
+		this.behaviorIndex = behaviorIndex;
+		this.renderCount = renderCount;
+	}
+
+	/**
+	 * @return component path
+	 */
+	public String getComponentPath()
+	{
+		return componentPath;
+	}
+
+	/**
+	 * @return listener interface name
+	 */
+	public String getListenerInterface()
+	{
+		return listenerInterface;
+	}
+
+	/**
+	 * @return behavior index
+	 */
+	public Integer getBehaviorIndex()
+	{
+		return behaviorIndex;
+	}
+	
+	/**
+	 * 
+	 * @return render count
+	 */
+	public Integer getRenderCount()
+	{
+		return renderCount;
+	}
+
+	private static final String TMP_PLACEHOLDER = "[[[[[[[WICKET[[TMP]]DASH]]" + Math.random() +
+		"]]]]";
+
+	private static String encodeComponentPath(String path)
+	{
+		if (path != null)
+		{
+			path = path.replace("" + SEPARATOR, TMP_PLACEHOLDER);
+			path = path.replace(':', SEPARATOR);
+			path = path.replace(TMP_PLACEHOLDER, "" + SEPARATOR + SEPARATOR);
+			return path;
+		}
+		else
+		{
+			return null;
+		}
+	}
+
+	private static String decodeComponentPath(String path)
+	{
+		if (path != null)
+		{
+			path = path.replace("" + SEPARATOR + SEPARATOR, TMP_PLACEHOLDER);
+			path = path.replace(SEPARATOR, ':');
+			path = path.replace(TMP_PLACEHOLDER, "" + SEPARATOR);
+			return path;
+		}
+		else
+		{
+			return null;
+		}
+	}
+
+	@Override
+	public String toString()
+	{
+		StringBuilder result = new StringBuilder();
+		
+		if (renderCount != null)
+		{
+			result.append(renderCount);
+			result.append(BEHAVIOR_INDEX_SEPARATOR);
+		}
+		
+		result.append(listenerInterface);
+
+		if (behaviorIndex != null)
+		{
+			result.append(BEHAVIOR_INDEX_SEPARATOR);
+			result.append(behaviorIndex);
+		}
+		result.append(SEPARATOR);
+		result.append(encodeComponentPath(componentPath));
+
+		return result.toString();
+	}
+
+	/**
+	 * Method that rigidly checks if the string consists of digits only.
+	 * 
+	 * @param string
+	 * @return whether the string consists of digits only
+	 */
+	private static boolean isNumber(String string)
+	{
+		if (string == null || string.length() == 0)
+		{
+			return false;
+		}
+		for (int i = 0; i < string.length(); ++i)
+		{
+			if (Character.isDigit(string.charAt(i)) == false)
+			{
+				return false;
+			}
+		}
+		return true;
+	}
+	
+	/**
+	 * Parses the given string.
+	 * 
+	 * @param string
+	 * @return component info or <code>null</code> if the string is not in correct format.
+	 */
+	public static ComponentInfo parse(String string)
+	{
+		if (Strings.isEmpty(string))
+		{
+			return null;
+		}
+		int i = string.indexOf(SEPARATOR);
+		if (i == -1)
+		{
+			return null;
+		}
+		else
+		{
+			String listenerInterface = string.substring(0, i);
+			String componentPath = decodeComponentPath(string.substring(i + 1));
+
+			if (Strings.isEmpty(listenerInterface))
+			{
+				return null;
+			}
+			if (Strings.isEmpty(componentPath))
+			{
+				return null;
+			}
+
+			Integer behaviorIndex = null;
+			Integer renderCount = null;
+			
+			String listenerParts[] = Strings.split(listenerInterface, BEHAVIOR_INDEX_SEPARATOR);
+			if (listenerParts.length == 2)
+			{
+				if (isNumber(listenerParts[0]))
+				{
+					renderCount = Integer.valueOf(listenerParts[0]);
+					listenerInterface = listenerParts[1];
+				}
+				else if (isNumber(listenerParts[1]))
+				{
+					listenerInterface = listenerParts[0];
+					behaviorIndex = Integer.valueOf(listenerParts[1]);									
+				}
+				else
+				{
+					return null;
+				}
+			}
+			else if (listenerParts.length == 3)
+			{
+				if (!isNumber(listenerParts[0]) && !isNumber(listenerParts[1]))
+				{
+					return null;
+				}
+				renderCount = Integer.valueOf(listenerParts[0]);
+				listenerInterface = listenerParts[1];
+				behaviorIndex = Integer.valueOf(listenerParts[2]);
+			}
+			else if (listenerParts.length != 1)
+			{
+				return null;
+			}
+
+			return new ComponentInfo(renderCount, listenerInterface, componentPath, behaviorIndex);
+		}
+	}
+
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/info/PageComponentInfo.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/info/PageComponentInfo.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/info/PageComponentInfo.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/info/PageComponentInfo.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.wicket.ng.request.mapper.info;
+
+import org.apache.wicket.ng.util.lang.Check;
+import org.apache.wicket.ng.util.string.Strings;
+
+/**
+ * Encapsulates both page and component info.
+ * Rendered in form of &lt;pageInfo&gt:&lt;componentInfo&gt;
+ * 
+ * @author Matej Knopp
+ */
+public class PageComponentInfo
+{
+	private final PageInfo pageInfo;
+	private final ComponentInfo componentInfo;
+
+	private static final char SEPARATOR = '-';
+	
+	/**
+	 * Construct.
+	 * @param pageInfo
+	 * @param componentInfo
+	 */
+	public PageComponentInfo(PageInfo pageInfo, ComponentInfo componentInfo)
+	{
+		Check.argumentNotNull(pageInfo, "pageInfo");
+		
+		this.pageInfo = pageInfo;
+		this.componentInfo = componentInfo;
+	}
+
+	/**
+	 * @return page info instance
+	 */
+	public PageInfo getPageInfo()
+	{
+		return pageInfo;
+	}
+	
+	/**
+	 * @return component info instance or <code>null</code>
+	 */
+	public ComponentInfo getComponentInfo()
+	{
+		return componentInfo;
+	}
+	
+	@Override
+	public String toString()
+	{
+		StringBuilder result = new StringBuilder();
+		if (pageInfo != null)
+		{
+			result.append(pageInfo.toString());
+		}		
+		if (componentInfo != null)
+		{
+			result.append(SEPARATOR);
+			result.append(componentInfo);
+		}
+		
+		return result.toString();
+	}
+	
+	/**
+	 * Parses the given string
+	 * 
+	 * @param s
+	 * @return {@link PageComponentInfo} or <code>null</code> if the string is not in valid format.
+	 */
+	public static PageComponentInfo parse(String s)
+	{
+		if (Strings.isEmpty(s))
+		{
+			return null;
+		}
+		
+		final PageInfo pageInfo;
+		final ComponentInfo componentInfo;
+		
+		int i = s.indexOf(SEPARATOR);
+		if (i == -1)
+		
+		{
+			pageInfo = PageInfo.parse(s);
+			componentInfo = null;
+		}
+		else
+		{
+			pageInfo = PageInfo.parse(s.substring(0, i));
+			componentInfo = ComponentInfo.parse(s.substring(i + 1));	
+		}
+			
+		if (pageInfo == null)
+		{
+			return null;
+		}
+		
+		return new PageComponentInfo(pageInfo, componentInfo);
+	}
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/info/PageInfo.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/info/PageInfo.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/info/PageInfo.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/info/PageInfo.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.wicket.ng.request.mapper.info;
+
+import org.apache.wicket.ng.request.component.RequestablePage;
+import org.apache.wicket.ng.util.lang.Check;
+import org.apache.wicket.ng.util.string.Strings;
+
+/**
+ * Possible string representation of PageInfo:
+ * <ul>
+ * <li>pageId
+ * </ul>
+ * 
+ * @author Matej Knopp
+ */
+public class PageInfo
+{
+	private final Integer pageId;
+
+	/**
+	 * Construct.
+	 * 
+	 * @param pageId
+	 */
+	public PageInfo(Integer pageId)
+	{
+		this.pageId = pageId;
+	}
+
+	/**
+	 * Construct.
+	 */
+	public PageInfo()
+	{
+		this((Integer)null);
+	}
+
+	/**
+	 * Construct.
+	 * 
+	 * @param page
+	 */
+	public PageInfo(RequestablePage page)
+	{
+		Check.argumentNotNull(page, "page");
+
+		this.pageId = page.getPageId();
+	}
+
+	/**
+	 * @return page id
+	 */
+	public Integer getPageId()
+	{
+		return pageId;
+	}
+
+	/**
+	 * <ul>
+	 * <li>pageId
+	 * <li>pageId.version
+	 * <li>pageMap (only in if pagemap starts with a letter)
+	 * <li>.pageMap
+	 * <li>pageMap.pageId (only in if pageMap name starts with a letter)
+	 * <li>pageMap.pageId.version
+	 * </ul>
+	 */
+	@Override
+	public String toString()
+	{
+		if (getPageId() == null)
+		{
+			return "";
+		}
+		else
+		{
+			return getPageId().toString();
+		}
+	}
+
+
+	/**
+	 * @param src
+	 * @return page insfo instance or <code>null</code> if the string couldn't have been parsed
+	 */
+	public static PageInfo parse(String src)
+	{
+		if (Strings.isEmpty(src))
+		{
+			return new PageInfo();
+		}
+		else
+		{
+			try
+			{
+				return new PageInfo(Integer.valueOf(src));
+			}
+			catch (NumberFormatException e)
+			{
+				return null;
+			}
+		}
+	}
+};

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/Mount.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/Mount.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/Mount.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/Mount.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,31 @@
+package org.apache.wicket.ng.request.mapper.mount;
+
+import org.apache.wicket.ng.request.Url;
+
+public class Mount
+{
+    private final Url url;
+    private MountParameters mountParameters = new MountParameters();
+
+    public Mount(Url url)
+    {
+        this.url = url;
+    }
+
+    public void setMountParameters(MountParameters mountParameters)
+    {
+        this.mountParameters = mountParameters;
+    }
+
+    public MountParameters getMountParameters()
+    {
+        return mountParameters;
+    }
+
+    public Url getUrl()
+    {
+        return url;
+    }
+
+
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/MountMapper.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/MountMapper.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/MountMapper.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/MountMapper.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,125 @@
+package org.apache.wicket.ng.request.mapper.mount;
+
+import org.apache.wicket.ng.request.Request;
+import org.apache.wicket.ng.request.RequestHandler;
+import org.apache.wicket.ng.request.RequestMapper;
+import org.apache.wicket.ng.request.Url;
+import org.apache.wicket.ng.request.mapper.AbstractMapper;
+import org.apache.wicket.ng.util.lang.Check;
+import org.apache.wicket.ng.util.string.StringValue;
+
+/**
+ * {@link RequestMapper} that can mount requests onto urls. TODO docs and unit test
+ * 
+ * @author igor.vaynberg
+ */
+public class MountMapper extends AbstractMapper
+{
+    private final String[] mountSegments;
+    private final MountedRequestMapper mapper;
+
+    public MountMapper(String mountPath, MountedRequestMapper mapper)
+    {
+        Check.argumentNotEmpty(mountPath, "mountPath");
+        Check.argumentNotNull(mapper, "mapper");
+
+        this.mountSegments = getMountSegments(mountPath);
+        this.mapper = mapper;
+    }
+
+    public MountMapper(String mountPath, RequestMapper mapper)
+    {
+        Check.argumentNotEmpty(mountPath, "mountPath");
+        Check.argumentNotNull(mapper, "mapper");
+
+        this.mountSegments = getMountSegments(mountPath);
+        this.mapper = new UnmountedMapperAdapter(mapper);
+    }
+
+    public MountMapper(String mountPath, RequestHandler handler)
+    {
+        Check.argumentNotEmpty(mountPath, "mountPath");
+        Check.argumentNotNull(handler, "handler");
+
+        this.mountSegments = getMountSegments(mountPath);
+        this.mapper = new UnmountedRequestHandlerAdapter(handler);
+    }
+
+    public int getCompatibilityScore(Request request)
+    {
+        if (urlStartsWith(request.getUrl(), mountSegments))
+        {
+            return mountSegments.length + mapper.getCompatibilityScore(dismountRequest(request));
+        }
+        else
+        {
+            return 0;
+        }
+    }
+
+    private Request dismountRequest(Request request)
+    {
+        Url dismountedUrl = new Url(request.getUrl());
+        dismountedUrl.removeLeadingSegments(mountSegments.length);
+        return request.requestWithUrl(dismountedUrl);
+    }
+
+
+    public final RequestHandler mapRequest(Request request)
+    {
+        final Url url = request.getUrl();
+
+        if (url.getSegments().size() >= mountSegments.length && urlStartsWith(url, mountSegments))
+        {
+            MountParameters params = new MountParameters();
+            for (int i = 0; i < mountSegments.length; i++)
+            {
+                String placeholder = getPlaceholder(mountSegments[i]);
+                if (placeholder != null)
+                {
+                    params.setValue(placeholder, StringValue.valueOf(url.getSegments().get(i)));
+                }
+            }
+
+            return mapper.mapRequest(dismountRequest(request), params);
+        }
+
+        return null;
+    }
+
+    public Url mapHandler(RequestHandler handler)
+    {
+        Mount mount = mapper.mapHandler(handler);
+
+        if (mount == null)
+        {
+            return null;
+        }
+
+        // TODO
+        // Check.notNull(mount.getUrl());
+        // Check.notNull(mount.getMountParameters());
+
+        for (int i = mountSegments.length; i > 0; i--)
+        {
+            String segment = mountSegments[i - 1];
+            String placeholder = getPlaceholder(segment);
+            String replacement = segment;
+
+            if (placeholder != null)
+            {
+                replacement = mount.getMountParameters().getValue(placeholder).toString();
+                if (replacement == null)
+                {
+                    throw new IllegalStateException();// TODO message
+                }
+            }
+
+            mount.getUrl().getSegments().add(0, replacement);
+        }
+
+        return mount.getUrl();
+    }
+
+
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/MountParameters.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/MountParameters.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/MountParameters.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/MountParameters.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,74 @@
+package org.apache.wicket.ng.request.mapper.mount;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.wicket.ng.util.string.StringValue;
+
+/**
+ * Mount parameters
+ * 
+ * TODO docs and unit test
+ * 
+ * @author igor.vaynberg
+ * 
+ */
+public class MountParameters
+{
+    private final Map<String, String> map = new HashMap<String, String>();
+
+    public final StringValue getValue(String parameterName)
+    {
+        return StringValue.valueOf(map.get(parameterName));
+    }
+
+    public final void setValue(String parameterName, StringValue value)
+    {
+        map.put(parameterName, value.toString());
+    }
+
+    public final Collection<String> getParameterNames()
+    {
+        return map.keySet();
+    }
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((map == null) ? 0 : map.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        MountParameters other = (MountParameters)obj;
+        if (map == null)
+        {
+            if (other.map != null)
+                return false;
+        }
+        else if (!map.equals(other.map))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString()
+    {
+        return "MountParameters [" + map + "]";
+    }
+    
+    
+    
+    
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/MountedRequestMapper.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/MountedRequestMapper.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/MountedRequestMapper.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/MountedRequestMapper.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,53 @@
+package org.apache.wicket.ng.request.mapper.mount;
+
+import org.apache.wicket.ng.request.Request;
+import org.apache.wicket.ng.request.RequestHandler;
+
+/**
+ * TODO javadoc, explain "parameters resolved from the mount"
+ * 
+ * @author igor.vaynberg
+ * 
+ */
+public interface MountedRequestMapper
+{
+    /**
+     * Returns {@link RequestHandler} for the request or <code>null</code> if the encoder does not
+     * recognize the URL.
+     * 
+     * @param request
+     *            provides access to request data (i.e. Url and Parameters)
+     * @param mountParams
+     *            parameters resolved from the mount
+     * @return RequestHandler instance or <code>null</code>
+     */
+    RequestHandler mapRequest(Request request, MountParameters mountParams);
+
+    /**
+     * Returns the score representing how compatible this request mapper is to processing the given
+     * request. When a request comes in all mappers are scored and are tried in order from highest
+     * score to lowest.
+     * <p>
+     * A good criteria for calculating the score is the number of matched url segments. For example
+     * when there are two encoders for mounted page, one mapped to <code>/foo</code> another to
+     * <code>/foo/bar</code> and the incomming reqest URL is </code>/foo/bar/baz</code>, the encoder
+     * mapped to <code>/foo/bar</code> will handle the request first as it has matching segments
+     * count of 2 while the first one has only matching segments count of 1.
+     * <p>
+     * Note that the method can return value greater then zero even if the encoder can not decode
+     * the request.
+     * 
+     * @param request
+     * @return count of matching segments
+     */
+    int getCompatibilityScore(Request request);
+
+    /**
+     * Returns the {@link Mount} for given {@link RequestHandler} or <code>null</code> if the
+     * encoder does not recognize the request handler.
+     * 
+     * @param requestHandler
+     * @return Url instance or <code>null</code>.
+     */
+    Mount mapHandler(RequestHandler requestHandler);
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/UnmountedMapperAdapter.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/UnmountedMapperAdapter.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/UnmountedMapperAdapter.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/UnmountedMapperAdapter.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,45 @@
+package org.apache.wicket.ng.request.mapper.mount;
+
+import org.apache.wicket.ng.request.Request;
+import org.apache.wicket.ng.request.RequestHandler;
+import org.apache.wicket.ng.request.RequestMapper;
+import org.apache.wicket.ng.request.Url;
+
+/**
+ * Adapts a {@link RequestMapper} to be used as a {@link MountedRequestMapper}
+ * 
+ * TODO javadoc
+ * 
+ * @author igor.vaynberg
+ * 
+ */
+class UnmountedMapperAdapter implements MountedRequestMapper
+{
+    private final RequestMapper mapper;
+
+    public UnmountedMapperAdapter(RequestMapper mapper)
+    {
+        super();
+        this.mapper = mapper;
+    }
+
+    public int getCompatibilityScore(Request request)
+    {
+        return mapper.getCompatibilityScore(request);
+    }
+
+    public Mount mapHandler(RequestHandler requestHandler)
+    {
+        Url url = mapper.mapHandler(requestHandler);
+        if (url != null)
+        {
+            return new Mount(url);
+        }
+        return null;
+    }
+
+    public RequestHandler mapRequest(Request request, MountParameters mountParams)
+    {
+        return mapper.mapRequest(request);
+    }
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/UnmountedRequestHandlerAdapter.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/UnmountedRequestHandlerAdapter.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/UnmountedRequestHandlerAdapter.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/mount/UnmountedRequestHandlerAdapter.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,43 @@
+package org.apache.wicket.ng.request.mapper.mount;
+
+import org.apache.wicket.ng.request.Request;
+import org.apache.wicket.ng.request.RequestHandler;
+import org.apache.wicket.ng.request.Url;
+
+/**
+ * Adapts a singleton {@link RequestHandler} instance to {@link MountedRequestMapper}
+ * 
+ * TODO javadoc
+ * 
+ * @author igor.vaynberg
+ * 
+ */
+class UnmountedRequestHandlerAdapter implements MountedRequestMapper
+{
+    private final RequestHandler handler;
+
+    public UnmountedRequestHandlerAdapter(RequestHandler handler)
+    {
+        this.handler = handler;
+    }
+
+    public int getCompatibilityScore(Request request)
+    {
+        return 0;
+    }
+
+    public Mount mapHandler(RequestHandler requestHandler)
+    {
+        if (requestHandler.equals(handler))
+        {
+            return new Mount(new Url());
+        }
+        return null;
+    }
+
+    public RequestHandler mapRequest(Request request, MountParameters mountParams)
+    {
+        return handler;
+    }
+
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/parameters/PageParametersEncoder.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/parameters/PageParametersEncoder.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/parameters/PageParametersEncoder.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/parameters/PageParametersEncoder.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.wicket.ng.request.mapper.parameters;
+
+import org.apache.wicket.ng.request.Request;
+import org.apache.wicket.ng.request.RequestHandler;
+import org.apache.wicket.ng.request.Url;
+import org.apache.wicket.ng.request.component.PageParameters;
+
+/**
+ * Encoder that can encode and decode {@link PageParameters} to/from URL.
+ * 
+ * @author Matej Knopp
+ */
+public interface PageParametersEncoder
+{
+	/**
+	 * Encode the given {@link PageParameters} instance into URL. The URL will be then merged with
+	 * the URL generated for {@link RequestHandler}.
+	 * 
+	 * @param pageParameters
+	 * @return Url generated from the page parameters
+	 */
+	public Url encodePageParameters(PageParameters pageParameters);
+
+	/**
+	 * Decodes the given URL to {@link PageParameters}. The URL will have all
+	 * {@link RequestHandler} specified segments/parameters stripped.
+	 * 
+	 * @param request
+	 * @return {@link PageParameters} instance
+	 */
+	public PageParameters decodePageParameters(Request requuest);
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/parameters/SimplePageParametersEncoder.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/parameters/SimplePageParametersEncoder.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/parameters/SimplePageParametersEncoder.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/mapper/parameters/SimplePageParametersEncoder.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.wicket.ng.request.mapper.parameters;
+
+import java.util.List;
+
+import org.apache.wicket.ng.request.Request;
+import org.apache.wicket.ng.request.Url;
+import org.apache.wicket.ng.request.Url.QueryParameter;
+import org.apache.wicket.ng.request.component.PageParameters;
+import org.apache.wicket.ng.util.string.StringValue;
+
+/**
+ * Simple encoder with direct indexed/named parameters mapping.
+ * 
+ * @author Matej Knopp
+ */
+public class SimplePageParametersEncoder implements PageParametersEncoder
+{
+	/**
+	 * Construct.
+	 */
+	public SimplePageParametersEncoder()
+	{
+	}
+
+	public PageParameters decodePageParameters(Request request)
+	{
+		PageParameters parameters = new PageParameters();
+		
+		int i = 0;
+		for (String s : request.getUrl().getSegments())
+		{
+			parameters.setIndexedParameter(i, s);
+			++i;
+		}
+		
+		for (QueryParameter p : request.getUrl().getQueryParameters())
+		{
+			parameters.addNamedParameter(p.getName(), p.getValue());
+		}
+		
+		return parameters;
+	}
+
+	public Url encodePageParameters(PageParameters pageParameters)
+	{
+		Url url = new Url();
+		
+		for (int i = 0; i < pageParameters.getIndexedParamsCount(); ++i)
+		{
+			url.getSegments().add(pageParameters.getIndexedParameter(i).toString());
+		}
+		
+		for (String key : pageParameters.getNamedParameterKeys())
+		{
+			List<StringValue> values = pageParameters.getNamedParameters(key);
+			if (values != null)
+			{
+				for (StringValue value : values)
+				{
+					QueryParameter param = new QueryParameter(key, value.toString());
+					url.getQueryParameters().add(param);
+				}
+			}
+		}
+		
+		return url;
+	}
+
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/parameter/CombinedRequestParametersAdapter.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/parameter/CombinedRequestParametersAdapter.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/parameter/CombinedRequestParametersAdapter.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/parameter/CombinedRequestParametersAdapter.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,76 @@
+package org.apache.wicket.ng.request.parameter;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.wicket.ng.request.RequestParameters;
+import org.apache.wicket.ng.util.string.StringValue;
+
+public class CombinedRequestParametersAdapter implements RequestParameters
+{
+	private final RequestParameters parameters[];
+
+	public CombinedRequestParametersAdapter(RequestParameters ... parameters)
+	{
+		if (parameters == null)
+		{
+			throw new IllegalStateException("Argument 'parameters' may not be null");
+		}
+		this.parameters = parameters;
+	}
+
+	public Set<String> getParameterNames()
+	{
+		Set<String> result = new HashSet<String>();
+		for (RequestParameters p : parameters)
+		{
+			result.addAll(p.getParameterNames());
+		}
+		return Collections.unmodifiableSet(result);
+	}
+
+	public StringValue getParameterValue(String name)
+	{
+		for (RequestParameters p : parameters)
+		{
+			StringValue value = p.getParameterValue(name);
+			if (!value.isNull())
+			{
+				return value;
+			}
+		}
+		return StringValue.valueOf((String)null);
+	}
+
+	public List<StringValue> getParameterValues(String name)
+	{
+		List<StringValue> result = new ArrayList<StringValue>();
+		for (RequestParameters p : parameters)
+		{
+			List<StringValue> values = p.getParameterValues(name);
+			if (values != null)
+			{
+				for (StringValue v : values)
+				{
+					if (!result.contains(v))
+					{
+						result.add(v);
+					}
+				}
+			}
+		}
+		
+		if (result.isEmpty())
+		{
+			return null;
+		}
+		else
+		{
+			return Collections.unmodifiableList(result);
+		}
+	}
+
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/parameter/EmptyRequestParameters.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/parameter/EmptyRequestParameters.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/parameter/EmptyRequestParameters.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/parameter/EmptyRequestParameters.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,34 @@
+package org.apache.wicket.ng.request.parameter;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.wicket.ng.request.RequestParameters;
+import org.apache.wicket.ng.util.string.StringValue;
+
+public class EmptyRequestParameters implements RequestParameters
+{
+
+	private EmptyRequestParameters()
+	{
+	}
+	
+	public static EmptyRequestParameters INSTANCE = new EmptyRequestParameters();
+
+	public Set<String> getParameterNames()
+	{
+		return Collections.emptySet();
+	}
+
+	public StringValue getParameterValue(String name)
+	{
+		return StringValue.valueOf((String)null);
+	}
+
+	public List<StringValue> getParameterValues(String name)
+	{
+		return null;
+	}
+
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/parameter/UrlRequestParametersAdapter.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/parameter/UrlRequestParametersAdapter.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/parameter/UrlRequestParametersAdapter.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/parameter/UrlRequestParametersAdapter.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.wicket.ng.request.parameter;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.wicket.ng.request.RequestParameters;
+import org.apache.wicket.ng.request.Url;
+import org.apache.wicket.ng.request.Url.QueryParameter;
+import org.apache.wicket.ng.util.lang.Check;
+import org.apache.wicket.ng.util.lang.Objects;
+import org.apache.wicket.ng.util.string.StringValue;
+
+/**
+ * Utility class that expresses query parameters from {@link Url} as {@link RequestParameters}.
+ * 
+ * @author Matej Knopp
+ */
+public class UrlRequestParametersAdapter implements RequestParameters
+{
+	private final Url url;
+
+	/**
+	 * Construct.
+	 * @param url
+	 */
+	public UrlRequestParametersAdapter(Url url)
+	{
+		Check.argumentNotNull(url, "url");
+		
+		this.url = url;
+	}
+	
+	public Set<String> getParameterNames()
+	{
+		Set<String> result = new HashSet<String>();
+		for (QueryParameter parameter : url.getQueryParameters())
+		{
+			result.add(parameter.getName());
+		}
+		return Collections.unmodifiableSet(result);
+	}
+
+	public StringValue getParameterValue(String name)
+	{
+		return url.getQueryParameterValue(name);
+	}
+
+	public List<StringValue> getParameterValues(String name)
+	{
+		List<StringValue> values = null;
+		for (QueryParameter parameter : url.getQueryParameters())
+		{
+			if (Objects.equal(name, parameter.getName()))
+			{
+				if (values == null)
+				{
+					values = new ArrayList<StringValue>();
+				}
+				values.add(StringValue.valueOf(parameter.getValue()));
+			}
+		}
+		return values != null ? Collections.unmodifiableList(values) : null;
+	}
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/response/BufferedWebResponse.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/response/BufferedWebResponse.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/response/BufferedWebResponse.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/response/BufferedWebResponse.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,249 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.wicket.ng.request.response;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.Cookie;
+
+import org.apache.wicket.ng.WicketRuntimeException;
+import org.apache.wicket.ng.util.lang.Check;
+
+/**
+ * Subclass of {@link WebResponse} that buffers the actions and performs those on another response.
+ * 
+ * @see #writeTo(WebResponse)
+ * 
+ * @author Matej Knopp
+ */
+public abstract class BufferedWebResponse extends WebResponse
+{
+
+	/**
+	 * Construct.
+	 */
+	public BufferedWebResponse()
+	{
+	}
+
+	private static class CookieEntry
+	{
+		Cookie cookie;
+		boolean add;
+	}
+
+	private List<CookieEntry> cookieEntries = new ArrayList<CookieEntry>();
+
+	@Override
+	public void addCookie(Cookie cookie)
+	{
+		CookieEntry entry = new CookieEntry();
+		entry.cookie = cookie;
+		entry.add = true;
+		cookieEntries.add(entry);
+	}
+
+	@Override
+	public void clearCookie(Cookie cookie)
+	{
+		CookieEntry entry = new CookieEntry();
+		entry.cookie = cookie;
+		entry.add = false;
+		cookieEntries.add(entry);
+	}
+
+	private Long contentLength = null;
+
+	@Override
+	public void setContentLength(long length)
+	{
+		contentLength = length;
+	}
+
+	private String contentType = null;
+
+	@Override
+	public void setContentType(String mimeType)
+	{
+		contentType = mimeType;
+	}
+
+	private Map<String, Long> dateHeaders = new HashMap<String, Long>();
+
+	@Override
+	public void setDateHeader(String name, long date)
+	{
+		dateHeaders.put(name, date);
+	}
+
+	private Map<String, String> headers = new HashMap<String, String>();
+
+	@Override
+	public void setHeader(String name, String value)
+	{
+		headers.put(name, value);
+	}
+
+	private StringBuilder builder;
+	private ByteArrayOutputStream stream;
+
+	@Override
+	public void write(CharSequence sequence)
+	{
+		if (stream != null)
+		{
+			throw new IllegalStateException(
+				"Can't call write(CharSequence) after write(byte[]) has been called.");
+		}
+
+		if (builder == null)
+		{
+			builder = new StringBuilder(4096);
+		}
+
+		builder.append(sequence);
+	}
+
+	@Override
+	public void write(byte[] array)
+	{
+		if (builder != null)
+		{
+			throw new IllegalStateException(
+				"Can't call write(byte[]) after write(CharSequence) has been called.");
+		}
+		if (stream == null)
+		{
+			stream = new ByteArrayOutputStream(array.length);
+		}
+		try
+		{
+			stream.write(array);
+		}
+		catch (IOException e)
+		{
+			throw new WicketRuntimeException(e);
+		}
+	}
+
+	private String redirectUrl = null;
+
+	@Override
+	public void sendRedirect(String url)
+	{
+		this.redirectUrl = url;
+	}
+
+	private Integer statusCode = null;
+
+	@Override
+	public void setStatus(int sc)
+	{
+		this.statusCode = sc;
+	}
+
+	/**
+	 * Writes the content of the buffer to the specified response. Also sets the properties and and
+	 * headers.
+	 * 
+	 * @param response
+	 */
+	public void writeTo(final WebResponse response)
+	{
+		Check.argumentNotNull(response, "response");
+
+		for (CookieEntry e : cookieEntries)
+		{
+			if (e.add)
+			{
+				response.addCookie(e.cookie);
+			}
+			else
+			{
+				response.clearCookie(e.cookie);
+			}
+		}
+		if (contentLength != null)
+		{
+			response.setContentLength(contentLength);
+		}
+		if (contentType != null)
+		{
+			response.setContentType(contentType);
+		}
+		for (String s : dateHeaders.keySet())
+		{
+			response.setDateHeader(s, dateHeaders.get(s));
+		}
+		for (String s : headers.keySet())
+		{
+			response.setHeader(s, headers.get(s));
+		}
+		if (statusCode != null)
+		{
+			response.setStatus(statusCode);
+		}
+		if (redirectUrl != null)
+		{
+			response.sendRedirect(redirectUrl);
+		}
+		if (builder != null)
+		{
+			response.write(builder);
+		}
+		else if (stream != null)
+		{
+			final boolean copied[] = { false };
+			try
+			{
+				// try to avoid copying the array
+				stream.writeTo(new OutputStream()
+				{
+					@Override
+					public void write(int b) throws IOException
+					{
+
+					}
+
+					@Override
+					public void write(byte[] b, int off, int len) throws IOException
+					{
+						if (off == 0 && len == b.length)
+						{
+							response.write(b);
+							copied[0] = true;
+						}
+					}
+				});
+			}
+			catch (IOException e1)
+			{
+				throw new WicketRuntimeException(e1);
+			}
+			if (copied[0] == false)
+			{
+				response.write(stream.toByteArray());
+			}
+		}
+	}
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/response/Response.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/response/Response.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/response/Response.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/response/Response.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.wicket.ng.request.response;
+
+/**
+ * Abstract base class for different implementations of response writing.
+ * <p>
+ * The implementation may not support calling both {@link #write(byte[])} and
+ * {@link #write(CharSequence)} on the same {@link Response} instance.
+ * 
+ * @author Matej Knopp
+ */
+public abstract class Response
+{
+	/**
+	 * Writes the {@link CharSequence} to output.
+	 * 
+	 * @param sequence
+	 * @throws IllegalStateException
+	 *             if {@link #write(byte[])} has already been called on this instance
+	 */
+	public abstract void write(CharSequence sequence);
+
+	/**
+	 * Writes the buffer to output.
+	 * 
+	 * @param array
+	 * @throws IllegalStateException
+	 *             if {@link #write(CharSequence)} has already been called on this instance
+	 */
+	public abstract void write(byte[] array);
+
+	/**
+	 * Closes the response
+	 */
+	public void close()
+	{
+	}
+	
+   
+	/**
+	 * Encodes the specified URL by including the session ID in it,
+     * or, if encoding is not needed, returns the URL unchanged.
+     * 
+	 * @param url
+	 * @return encoded URL
+	 */
+	public abstract String encodeURL(String url);
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/response/ServletWebResponse.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/response/ServletWebResponse.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/response/ServletWebResponse.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/response/ServletWebResponse.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.wicket.ng.request.response;
+
+import java.io.IOException;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.wicket.ng.WicketRuntimeException;
+import org.apache.wicket.ng.util.lang.Check;
+
+/**
+ * WebResponse that wraps a {@link ServletWebResponse}.
+ * @author Matej Knopp
+ */
+public class ServletWebResponse extends WebResponse
+{
+	private final HttpServletResponse httpServletResponse;
+
+	/**
+	 * Construct.
+
+	 * @param httpServletResponse
+	 */
+	public ServletWebResponse(HttpServletResponse httpServletResponse)
+	{
+		Check.argumentNotNull(httpServletResponse, "httpServletResponse");
+
+		this.httpServletResponse = httpServletResponse;
+	}
+
+	/**
+	 * Returns the wrapped response
+	 * 
+	 * @return wrapped response
+	 */
+	public final HttpServletResponse getHttpServletResponse()
+	{
+		return httpServletResponse;
+	}
+	
+	@Override
+	public void addCookie(Cookie cookie)
+	{
+		httpServletResponse.addCookie(cookie);
+	}
+
+	@Override
+	public void clearCookie(Cookie cookie)
+	{
+		cookie.setMaxAge(0);
+		cookie.setValue(null);
+		addCookie(cookie);
+	}
+
+	@Override
+	public void setContentLength(long length)
+	{
+		httpServletResponse.setContentLength((int)length);
+	}
+
+	@Override
+	public void setContentType(String mimeType)
+	{
+		httpServletResponse.setContentType(mimeType);
+	}
+
+	@Override
+	public void setDateHeader(String name, long date)
+	{
+		httpServletResponse.setDateHeader(name, date);
+	}
+
+	@Override
+	public void setHeader(String name, String value)
+	{
+		httpServletResponse.setHeader(name, value);
+	}
+
+	@Override
+	public void write(CharSequence sequence)
+	{		
+		try
+		{
+			httpServletResponse.getWriter().append(sequence);
+		}
+		catch (IOException e)
+		{
+			throw new WicketRuntimeException(e);
+		}
+	}
+
+	@Override
+	public void write(byte[] array)
+	{
+		try
+		{
+			httpServletResponse.getOutputStream().write(array);
+		}
+		catch (IOException e)
+		{
+			throw new WicketRuntimeException(e);
+		}
+	}
+
+	@Override
+	public void setStatus(int sc)
+	{
+		httpServletResponse.setStatus(sc);
+	}
+	
+	@Override
+	public String encodeURL(String url)
+	{
+		if (url != null)
+		{
+			if (url.length() > 0 && url.charAt(0) == '?')
+			{
+				// there is a bug in apache tomcat 5.5 where tomcat doesn't put sessionid to url
+				// when the URL starts with '?'. So we prepend the URL with ./ and remove it
+				// afterwards (unless some container prepends session id before './' or mangles
+				// the URL otherwise
+
+				String encoded = httpServletResponse.encodeURL("./" + url.toString());
+				if (encoded.startsWith("./"))
+				{
+					return encoded.substring(2);
+				}
+				else
+				{
+					return encoded;
+				}
+			}
+			else
+			{
+				return httpServletResponse.encodeURL(url.toString());
+			}
+		}		
+		return httpServletResponse.encodeURL(url);
+	}
+	
+	@Override
+	public void sendRedirect(String url)
+	{
+		url = httpServletResponse.encodeRedirectURL(url);
+		try
+		{
+			httpServletResponse.sendRedirect(url);
+		}
+		catch (IOException e)
+		{
+			throw new WicketRuntimeException(e);
+		}
+	}
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/response/StringResponse.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/response/StringResponse.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/response/StringResponse.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/response/StringResponse.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,35 @@
+package org.apache.wicket.ng.request.response;
+
+public class StringResponse extends Response
+{
+
+	private final StringBuilder builder = new StringBuilder();
+	
+	public StringResponse()
+	{
+	}
+
+	@Override
+	public String encodeURL(String url)
+	{
+		return url;
+	}
+
+	@Override
+	public void write(CharSequence sequence)
+	{
+		builder.append(sequence);
+	}
+
+	@Override
+	public void write(byte[] array)
+	{
+		throw new UnsupportedOperationException();
+	}
+		
+	@Override
+	public String toString()
+	{
+		return builder.toString();
+	}
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/response/WebResponse.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/response/WebResponse.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/response/WebResponse.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/response/WebResponse.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.wicket.ng.request.response;
+
+import javax.servlet.http.Cookie;
+
+import org.apache.wicket.ng.util.string.Strings;
+
+
+/**
+ * Base class for web-related responses.
+ * 
+ * @author Matej Knopp
+ */
+public abstract class WebResponse extends Response
+{
+	/**
+	 * Add a cookie to the web response
+	 * 
+	 * @param cookie
+	 */
+	public abstract void addCookie(final Cookie cookie);
+
+	/**
+	 * Convenience method for clearing a cookie.
+	 * 
+	 * @param cookie
+	 *            The cookie to set
+	 * @see WebResponse#addCookie(Cookie)
+	 */
+	public abstract void clearCookie(final Cookie cookie);
+
+	/**
+	 * Set a header to the string value in the servlet response stream.
+	 * 
+	 * @param name
+	 * @param value
+	 */
+	public abstract void setHeader(String name, String value);
+
+	/**
+	 * Set a header to the date value in the servlet response stream.
+	 * 
+	 * @param name
+	 * @param date
+	 */
+	public abstract void setDateHeader(String name, long date);
+
+	/**
+	 * Set the content length on the response, if appropriate in the subclass. This default
+	 * implementation does nothing.
+	 * 
+	 * @param length
+	 *            The length of the content
+	 */
+	public abstract void setContentLength(final long length);
+
+	/**
+	 * Set the content type on the response, if appropriate in the subclass. This default
+	 * implementation does nothing.
+	 * 
+	 * @param mimeType
+	 *            The mime type
+	 */
+	public abstract void setContentType(final String mimeType);
+
+	/**
+	 * Set the contents last modified time, if appropriate in the subclass. 
+	 * 
+	 * @param time
+	 *            The last modified time in milliseconds
+	 */
+	public void setLastModifiedTime(long time)
+	{
+		setDateHeader("Last-Modified", time);
+	}
+
+	/**
+	 * Convenience method for setting the content-disposition:attachment header. This header is used
+	 * if the response should prompt the user to download it as a file instead of opening in a
+	 * browser.
+	 * 
+	 * @param filename
+	 *            file name of the attachment
+	 */
+	public void setAttachmentHeader(String filename)
+	{
+		setHeader("Content-Disposition", "attachment" +
+			((!Strings.isEmpty(filename)) ? ("; filename=\"" + filename + "\"") : ""));
+	}
+
+	/**
+	 * Convenience method for setting the content-disposition:in;ine header. This header is used if
+	 * the response should be shown embedded in browser window while having custom file name when
+	 * user saves the response. browser.
+	 * 
+	 * @param filename
+	 *            file name of the attachment
+	 */
+	public void setInlineHeader(String filename)
+	{
+		setHeader("Content-Disposition", "inline" +
+			((!Strings.isEmpty(filename)) ? ("; filename=\"" + filename + "\"") : ""));
+	}
+
+	/**
+     * Sets the status code for this response.
+     * 
+     * @param sc status code 
+     */  
+	public abstract void setStatus(int sc);
+	
+	
+	/**
+	 * Redirects the response to specified URL. The implementation is responsible for properly
+	 * encoding the URL.
+	 * 
+	 * @param url
+	 */
+	public abstract void sendRedirect(String url);
+}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/url-format.txt
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/url-format.txt?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/url-format.txt (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/request/url-format.txt Sun Oct 11 00:02:25 2009
@@ -0,0 +1,62 @@
+NOTE - According to http://www.rfc-editor.org/rfc/rfc1738.txt ':' is not a valid
+       character in URL and must be encoded. 
+       In order for Wicket to produce valid URLs I've decided to use - as separator instead
+       This will only affect listener URLs anyway, which are not shown in location bar 
+       (except for bookmarkable listeners on stateless pages)
+       '-' in component name is encoded as '--'.
+
+
+NOT BOOKMARKABLE
+----------------
+
+Page Instance - Render (RenderPageRequestHandler)
+/wicket/page?2
+
+Page Instance - Listener (ListenerInterfaceRequestHandler)
+/wicket/page?2-5.click-foo-bar-baz   (5 is render count)
+
+Page Instance - Listener with behavior (ListenerInterfaceRequestHandler) 
+/wicket/page?2-5.click.1-foo-bar-baz   (5 is render count, 1 is behavior index)
+
+BOOKMARKABLE - NOT MOUNTED
+--------------------------
+
+Page Class - Render (BookmarkablePageRequestHandler)
+/wicket/bookmarkable/org.apache.wicket.MyPage
+/wicket/bookmarkable/org.apache.wicket.MyPage?pageMap
+(these will redirect to hybrid alternative if page is not stateless)
+
+Page Instance - Render Hybrid (RenderPageRequestHandler for pages that were created using bookmarkable URLs)
+/wicket/bookmarkable/org.apache.wicket.MyPage?2
+
+Page Instance - Bookmarkable Listener (BookmarkableListenerInterfaceRequestHandler)
+/wicket/bookmarkable/org.apache.wicket.MyPage?2-5.click-foo-bar-baz (5 is renderCount)
+/wicket/bookmarkable/org.apache.wicket.MyPage?2-5.click.1-foo-bar-baz (5 is renderCount, 1 is behavior index)
+(these will redirect to hybrid if page is not stateless)
+
+BOOKMARKABLE - MOUNTED
+----------------------
+
+Page Class - Render (BookmarkablePageRequestHandler for mounted pages)
+/mount/point
+/mount/point?pageMap
+(these will redirect to hybrid alternative if page is not stateless)
+
+Page Instance - Render Hybrid (RenderPageRequestHandler for mounted pages) 
+/mount/point?2
+
+Page Instance - Bookmarkable Listener (BookmarkableListenerInterfaceRequestHandler for mounted pages) 
+/mount/point?2-5.click-foo-bar-baz (5 is render count)
+/mount/point?2-5.click.1-foo-bar-baz (5 is render count, 1 is behavior index)
+(these will redirect to hybrid if page is not stateless)
+
+mount path can also contain parameter placeholders
+e.g. when mount path is /foo/${bar}/baz and the incoming url is /foo/abc/baz   the extracted page parameters will 
+contain named parameter bar="abc".
+
+RESOURCES
+---------
+/wicket/resource/org.apache.wicket.ResourceScope/resource/path.xyz?en_EN-style
+/mounted/resource?en_EN-style  (with locale and style)
+/mounted/resource
+

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/resource/Resource.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/resource/Resource.java?rev=823984&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/resource/Resource.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ng/resource/Resource.java Sun Oct 11 00:02:25 2009
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.wicket.ng.resource;
+
+import java.io.Serializable;
+import java.util.Locale;
+
+import org.apache.wicket.ng.request.Request;
+import org.apache.wicket.ng.request.component.PageParameters;
+import org.apache.wicket.ng.request.handler.resource.ResourceRequestHandler;
+import org.apache.wicket.ng.request.response.Response;
+import org.apache.wicket.ng.util.lang.Check;
+
+/**
+ * Resource is an object capable of writing output to response.
+ * 
+ * @author Matej Knopp
+ */
+public interface Resource extends Serializable
+{
+	/**
+	 * Attributes that are provided to resource in the {@link Resource#respond(Attributes)} method.
+	 * Attributes are set by the {@link ResourceRequestHandler}.
+	 * 
+	 * @author Matej Knopp
+	 */
+	public static class Attributes
+	{
+		private final Request request;
+		private final Response response;
+		private final Locale locale;
+		private final String style;
+		private final PageParameters parameters;
+
+		/**
+		 * Construct.
+		 * 
+		 * @param request
+		 * 
+		 * @param response
+		 * @param locale
+		 * @param style
+		 * @param parameters
+		 */
+		public Attributes(Request request, Response response, Locale locale, String style,
+			PageParameters parameters)
+		{
+			Check.argumentNotNull(request, "request");
+			Check.argumentNotNull(response, "response");
+			Check.argumentNotNull(locale, "locale");
+
+			this.request = request;
+			this.response = response;
+			this.locale = locale;
+			this.style = style;
+			this.parameters = parameters;
+		}
+
+		/**
+		 * Returns current request.
+		 * 
+		 * @return current request
+		 */
+		public Request getRequest()
+		{
+			return request;
+		}
+
+		/**
+		 * Returns current response. The resource must write output to the response.
+		 * 
+		 * @return response
+		 */
+		public Response getResponse()
+		{
+			return response;
+		}
+
+		/**
+		 * Returns requested locale. The locale is never null.
+		 * 
+		 * @return locale
+		 */
+		public Locale getLocale()
+		{
+			return locale;
+		}
+
+		/**
+		 * If specified returns requested style. The style is optional.
+		 * 
+		 * @return style or <code>null</code>
+		 */
+		public String getStyle()
+		{
+			return style;
+		}
+
+		/**
+		 * Returns additional parameters extracted from the request. If resource is created mounted
+		 * {@link ResourceReference}, this method returns all (indexed and query) parameters after
+		 * the mount path. For non mounted {@link ResourceReference}s this method will only return
+		 * the query parameters. For component specific resources the behavior depends on the
+		 * component.
+		 * 
+		 * @return page parameters or <code>null</code>
+		 */
+		public PageParameters getParameters()
+		{
+			return parameters;
+		}
+	};
+
+	/**
+	 * Renders this resource to response using the provided attributes.
+	 * 
+	 * @param attributes
+	 */
+	public void respond(Attributes attributes);
+}