You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by da...@apache.org on 2011/05/02 15:15:50 UTC
svn commit: r1098550 - in /wicket/trunk:
wicket-core/src/main/java/org/apache/wicket/request/mapper/
wicket-core/src/test/java/org/apache/wicket/request/mapper/
wicket-request/src/main/java/org/apache/wicket/request/mapper/
Author: dashorst
Date: Mon May 2 13:15:49 2011
New Revision: 1098550
URL: http://svn.apache.org/viewvc?rev=1098550&view=rev
Log:
MountedMapper now has optional parameter support. Thanks to Emond Papegaaij
Issue: WICKET-3660
Modified:
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/mapper/MountedMapper.java
wicket/trunk/wicket-core/src/test/java/org/apache/wicket/request/mapper/MountedMapperTest.java
wicket/trunk/wicket-request/src/main/java/org/apache/wicket/request/mapper/AbstractMapper.java
Modified: wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/mapper/MountedMapper.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/mapper/MountedMapper.java?rev=1098550&r1=1098549&r2=1098550&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/mapper/MountedMapper.java (original)
+++ wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/mapper/MountedMapper.java Mon May 2 13:15:49 2011
@@ -16,6 +16,9 @@
*/
package org.apache.wicket.request.mapper;
+import java.util.ArrayList;
+import java.util.List;
+
import org.apache.wicket.request.Request;
import org.apache.wicket.request.Url;
import org.apache.wicket.request.component.IRequestablePage;
@@ -31,6 +34,10 @@ import org.apache.wicket.util.lang.Args;
* <code>/mount/${foo}/path</code>. In that case the appropriate segment from the URL will be
* accessible as named parameter "foo" in the {@link PageParameters}. Similarly when the URL is
* constructed, the second segment will contain the value of the "foo" named page parameter.
+ * Optional parameters are denoted by using a # instead of $: <code>/mount/#{foo}/path/${bar}</code>
+ * has an optional {@code foo} parameter, a fixed {@code /path/} part and a required {@code bar}
+ * parameter. When in doubt, parameters are matched from left to right, where required parameters
+ * are matched before optional parameters, and optional parameters eager (from left to right).
* <p>
* Decodes and encodes the following URLs:
*
@@ -54,6 +61,72 @@ public class MountedMapper extends Abstr
{
private final IPageParametersEncoder pageParametersEncoder;
+ private static class MountPathSegment
+ {
+ private int segmentIndex;
+ private String fixedPart;
+ private int minParameters;
+ private int optionalParameters;
+
+ public MountPathSegment(int segmentIndex)
+ {
+ this.segmentIndex = segmentIndex;
+ }
+
+ public void setFixedPart(String fixedPart)
+ {
+ this.fixedPart = fixedPart;
+ }
+
+ public void addRequiredParameter()
+ {
+ minParameters++;
+ }
+
+ public void addOptionalParameter()
+ {
+ optionalParameters++;
+ }
+
+ public int getSegmentIndex()
+ {
+ return segmentIndex;
+ }
+
+ public String getFixedPart()
+ {
+ return fixedPart;
+ }
+
+ public int getMinParameters()
+ {
+ return minParameters;
+ }
+
+ public int getOptionalParameters()
+ {
+ return optionalParameters;
+ }
+
+ public int getMaxParameters()
+ {
+ return getOptionalParameters() + getMinParameters();
+ }
+
+ public int getFixedPartSize()
+ {
+ return getFixedPart() == null ? 0 : 1;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "(" + getSegmentIndex() + ") " + getMinParameters() + "-" + getMaxParameters() +
+ " " + (getFixedPart() == null ? "(end)" : getFixedPart());
+ }
+ }
+
+ private final List<MountPathSegment> pathSegments;
private final String[] mountSegments;
/** bookmarkable page class. */
@@ -82,7 +155,6 @@ public class MountedMapper extends Abstr
this(mountPath, pageClassProvider, new PageParametersEncoder());
}
-
/**
* Construct.
*
@@ -114,6 +186,39 @@ public class MountedMapper extends Abstr
this.pageParametersEncoder = pageParametersEncoder;
this.pageClassProvider = pageClassProvider;
mountSegments = getMountSegments(mountPath);
+ pathSegments = getPathSegments(mountSegments);
+ }
+
+ private List<MountPathSegment> getPathSegments(String[] segments)
+ {
+ List<MountPathSegment> ret = new ArrayList<MountPathSegment>();
+ int segmentIndex = 0;
+ MountPathSegment curPathSegment = new MountPathSegment(segmentIndex);
+ ret.add(curPathSegment);
+ for (String curSegment : segments)
+ {
+ if (isFixedSegment(curSegment))
+ {
+ curPathSegment.setFixedPart(curSegment);
+ curPathSegment = new MountPathSegment(segmentIndex + 1);
+ ret.add(curPathSegment);
+ }
+ else if (getPlaceholder(curSegment) != null)
+ {
+ curPathSegment.addRequiredParameter();
+ }
+ else
+ {
+ curPathSegment.addOptionalParameter();
+ }
+ segmentIndex++;
+ }
+ return ret;
+ }
+
+ private boolean isFixedSegment(String segment)
+ {
+ return getOptionalPlaceholder(segment) == null && getPlaceholder(segment) == null;
}
/**
@@ -136,36 +241,113 @@ public class MountedMapper extends Abstr
{
// try to extract page and component information from URL
PageComponentInfo info = getPageComponentInfo(url);
-
Class<? extends IRequestablePage> pageClass = getPageClass();
+ PageParameters pageParameters = extractPageParameters(request, url);
- // extract the PageParameters from URL if there are any
- PageParameters pageParameters = extractPageParameters(request, mountSegments.length,
- pageParametersEncoder);
+ return new UrlInfo(info, pageClass, pageParameters);
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /*
+ * extract the PageParameters from URL if there are any
+ */
+ private PageParameters extractPageParameters(Request request, Url url)
+ {
+ int[] matchedParameters = getMatchedSegmentSizes(url);
+ int total = 0;
+ for (int curMatchSize : matchedParameters)
+ total += curMatchSize;
+ PageParameters pageParameters = extractPageParameters(request, total, pageParametersEncoder);
- // check if there are placeholders in mount segments
- for (int i = 0; i < mountSegments.length; ++i)
+ int skippedParameters = 0;
+ for (int pathSegmentIndex = 0; pathSegmentIndex < pathSegments.size(); pathSegmentIndex++)
+ {
+ MountPathSegment curPathSegment = pathSegments.get(pathSegmentIndex);
+ int matchSize = matchedParameters[pathSegmentIndex] - curPathSegment.getFixedPartSize();
+ int optionalParameterMatch = matchSize - curPathSegment.getMinParameters();
+ for (int matchSegment = 0; matchSegment < matchSize; matchSegment++)
{
- String placeholder = getPlaceholder(mountSegments[i]);
+ if (pageParameters == null)
+ {
+ pageParameters = new PageParameters();
+ }
+
+ int curSegmentIndex = matchSegment + curPathSegment.getSegmentIndex();
+ String curSegment = mountSegments[curSegmentIndex];
+ String placeholder = getPlaceholder(curSegment);
+ String optionalPlaceholder = getOptionalPlaceholder(curSegment);
+ // extract the parameter from URL
if (placeholder != null)
{
- // extract the parameter from URL
- if (pageParameters == null)
- {
- pageParameters = new PageParameters();
- }
- pageParameters.add(placeholder, url.getSegments().get(i));
+ pageParameters.add(placeholder,
+ url.getSegments().get(curSegmentIndex - skippedParameters));
+ }
+ else if (optionalPlaceholder != null && optionalParameterMatch > 0)
+ {
+ pageParameters.add(optionalPlaceholder,
+ url.getSegments().get(curSegmentIndex - skippedParameters));
+ optionalParameterMatch--;
}
}
+ skippedParameters += curPathSegment.getMaxParameters() - matchSize;
+ }
+ return pageParameters;
+ }
- return new UrlInfo(info, pageClass, pageParameters);
+ @Override
+ protected boolean urlStartsWith(Url url, String... segments)
+ {
+ if (url == null)
+ {
+ return false;
}
else
{
- return null;
+ return getMatchedSegmentSizes(url) != null;
}
}
+ private int[] getMatchedSegmentSizes(Url url)
+ {
+ int[] ret = new int[pathSegments.size()];
+ int segmentIndex = 0;
+ int pathSegmentIndex = 0;
+ for (MountPathSegment curPathSegment : pathSegments.subList(0, pathSegments.size() - 1))
+ {
+ boolean foundFixedPart = false;
+ segmentIndex += curPathSegment.getMinParameters();
+ int max = Math.min(curPathSegment.getOptionalParameters() + 1,
+ url.getSegments().size() - segmentIndex);
+
+ for (int count = max - 1; count >= 0; count--)
+ {
+ if (url.getSegments()
+ .get(segmentIndex + count)
+ .equals(curPathSegment.getFixedPart()))
+ {
+ foundFixedPart = true;
+ segmentIndex += count + 1;
+ ret[pathSegmentIndex] = count + curPathSegment.getMinParameters() + 1;
+ break;
+ }
+ }
+ if (!foundFixedPart)
+ return null;
+ pathSegmentIndex++;
+ }
+ MountPathSegment lastSegment = pathSegments.get(pathSegments.size() - 1);
+ segmentIndex += lastSegment.getMinParameters();
+ if (segmentIndex > url.getSegments().size())
+ return null;
+ ret[pathSegmentIndex] = Math.min(lastSegment.getMaxParameters(), url.getSegments().size() -
+ segmentIndex + lastSegment.getMinParameters());
+ return ret;
+ }
+
protected PageParameters newPageParameters()
{
return new PageParameters();
@@ -186,14 +368,29 @@ public class MountedMapper extends Abstr
PageParameters copy = new PageParameters(info.getPageParameters());
+ int dropped = 0;
for (int i = 0; i < mountSegments.length; ++i)
{
String placeholder = getPlaceholder(mountSegments[i]);
+ String optionalPlaceholder = getOptionalPlaceholder(mountSegments[i]);
if (placeholder != null)
{
- url.getSegments().set(i, copy.get(placeholder).toString(""));
+ url.getSegments().set(i - dropped, copy.get(placeholder).toString(""));
copy.remove(placeholder);
}
+ else if (optionalPlaceholder != null)
+ {
+ if (copy.getNamedKeys().contains(optionalPlaceholder))
+ {
+ url.getSegments().set(i - dropped, copy.get(optionalPlaceholder).toString(""));
+ copy.remove(optionalPlaceholder);
+ }
+ else
+ {
+ url.getSegments().remove(i - dropped);
+ dropped++;
+ }
+ }
}
return encodePageParameters(url, copy, pageParametersEncoder);
Modified: wicket/trunk/wicket-core/src/test/java/org/apache/wicket/request/mapper/MountedMapperTest.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/test/java/org/apache/wicket/request/mapper/MountedMapperTest.java?rev=1098550&r1=1098549&r2=1098550&view=diff
==============================================================================
--- wicket/trunk/wicket-core/src/test/java/org/apache/wicket/request/mapper/MountedMapperTest.java (original)
+++ wicket/trunk/wicket-core/src/test/java/org/apache/wicket/request/mapper/MountedMapperTest.java Mon May 2 13:15:49 2011
@@ -65,6 +65,16 @@ public class MountedMapperTest extends A
}
};
+ private final MountedMapper optionPlaceholderEncoder = new MountedMapper(
+ "/some/#{param1}/path/${param2}/#{param3}", MockPage.class)
+ {
+ @Override
+ protected IMapperContext getContext()
+ {
+ return context;
+ }
+ };
+
/**
*
*/
@@ -493,6 +503,23 @@ public class MountedMapperTest extends A
assertEquals("p2", page.getPageParameters().get("param2").toString());
}
+ /** */
+ public void testPlaceholderDecodeWithIndexedParameters()
+ {
+ Url url = Url.parse("some/p1/path/p2/p3/p4");
+ IRequestHandler handler = placeholderEncoder.mapRequest(getRequest(url));
+
+ assertTrue(handler instanceof RenderPageRequestHandler);
+ IRequestablePage page = ((RenderPageRequestHandler)handler).getPage();
+
+ assertEquals(2, page.getPageParameters().getIndexedCount());
+ assertTrue(page.getPageParameters().getNamedKeys().size() == 2);
+ assertEquals("p1", page.getPageParameters().get("param1").toString());
+ assertEquals("p2", page.getPageParameters().get("param2").toString());
+ assertEquals("p3", page.getPageParameters().get(0).toString());
+ assertEquals("p4", page.getPageParameters().get(1).toString());
+ }
+
/**
*
*/
@@ -541,4 +568,128 @@ public class MountedMapperTest extends A
Url url = placeholderEncoder.mapHandler(handler);
assertEquals("some/p1/path/p2/i1/i2?1&a=b&b=c", url.toString());
}
+
+ /** */
+ public void testOptionPlaceholderDecode1()
+ {
+ Url url = Url.parse("some/p1/path/p2/p3");
+ IRequestHandler handler = optionPlaceholderEncoder.mapRequest(getRequest(url));
+
+ assertTrue(handler instanceof RenderPageRequestHandler);
+ IRequestablePage page = ((RenderPageRequestHandler)handler).getPage();
+
+ assertEquals(0, page.getPageParameters().getIndexedCount());
+ assertTrue(page.getPageParameters().getNamedKeys().size() == 3);
+ assertEquals("p1", page.getPageParameters().get("param1").toString());
+ assertEquals("p2", page.getPageParameters().get("param2").toString());
+ assertEquals("p3", page.getPageParameters().get("param3").toString());
+ }
+
+ /** */
+ public void testOptionPlaceholderDecodeEagerMatchParameters()
+ {
+ Url url = Url.parse("some/path/path/path");
+ IRequestHandler handler = optionPlaceholderEncoder.mapRequest(getRequest(url));
+
+ assertTrue(handler instanceof RenderPageRequestHandler);
+ IRequestablePage page = ((RenderPageRequestHandler)handler).getPage();
+
+ assertEquals(0, page.getPageParameters().getIndexedCount());
+ assertTrue(page.getPageParameters().getNamedKeys().size() == 2);
+ assertEquals("path", page.getPageParameters().get("param1").toString());
+ assertEquals("path", page.getPageParameters().get("param2").toString());
+ assertFalse("param3 should not be set",
+ page.getPageParameters().getNamedKeys().contains("param3"));
+ }
+
+ /** */
+ public void testOptionPlaceholderDecode2()
+ {
+ Url url = Url.parse("some/p1/path/p2");
+ IRequestHandler handler = optionPlaceholderEncoder.mapRequest(getRequest(url));
+
+ assertTrue(handler instanceof RenderPageRequestHandler);
+ IRequestablePage page = ((RenderPageRequestHandler)handler).getPage();
+
+ assertEquals(0, page.getPageParameters().getIndexedCount());
+ assertTrue(page.getPageParameters().getNamedKeys().size() == 2);
+ assertEquals("p1", page.getPageParameters().get("param1").toString());
+ assertEquals("p2", page.getPageParameters().get("param2").toString());
+ assertFalse("param3 should not be set",
+ page.getPageParameters().getNamedKeys().contains("param3"));
+ }
+
+ /** */
+ public void testOptionPlaceholderDecode3()
+ {
+ Url url = Url.parse("some/path/p2");
+ IRequestHandler handler = optionPlaceholderEncoder.mapRequest(getRequest(url));
+
+ assertTrue(handler instanceof RenderPageRequestHandler);
+ IRequestablePage page = ((RenderPageRequestHandler)handler).getPage();
+
+ assertEquals(0, page.getPageParameters().getIndexedCount());
+ assertTrue(page.getPageParameters().getNamedKeys().size() == 1);
+ assertFalse("param1 should not be set",
+ page.getPageParameters().getNamedKeys().contains("param1"));
+ assertEquals("p2", page.getPageParameters().get("param2").toString());
+ assertFalse("param3 should not be set",
+ page.getPageParameters().getNamedKeys().contains("param3"));
+ }
+
+ /** */
+ public void testOptionPlaceholderDecodeWithIndexParams()
+ {
+ Url url = Url.parse("some/path/p2/p3/p4");
+ IRequestHandler handler = optionPlaceholderEncoder.mapRequest(getRequest(url));
+
+ assertTrue(handler instanceof RenderPageRequestHandler);
+ IRequestablePage page = ((RenderPageRequestHandler)handler).getPage();
+
+ assertEquals(1, page.getPageParameters().getIndexedCount());
+ assertTrue(page.getPageParameters().getNamedKeys().size() == 2);
+ assertFalse("param1 should not be set",
+ page.getPageParameters().getNamedKeys().contains("param1"));
+ assertEquals("p2", page.getPageParameters().get("param2").toString());
+ assertEquals("p3", page.getPageParameters().get("param3").toString());
+ assertEquals("p4", page.getPageParameters().get(0).toString());
+ }
+
+ /** */
+ public void testOptionPlaceholderEncode1()
+ {
+ PageParameters parameters = new PageParameters();
+ parameters.set(0, "i1");
+ parameters.set(1, "i2");
+ parameters.set("a", "b");
+ parameters.set("b", "c");
+ parameters.set("param1", "p1");
+ parameters.set("param2", "p2");
+
+
+ PageProvider provider = new PageProvider(MockPage.class, parameters);
+ provider.setPageSource(context);
+ IRequestHandler handler = new BookmarkablePageRequestHandler(provider);
+ Url url = optionPlaceholderEncoder.mapHandler(handler);
+ assertEquals("some/p1/path/p2/i1/i2?a=b&b=c", url.toString());
+ }
+
+ /** */
+ public void testOptionPlaceholderEncode2()
+ {
+ PageParameters parameters = new PageParameters();
+ parameters.set(0, "i1");
+ parameters.set(1, "i2");
+ parameters.set("a", "b");
+ parameters.set("b", "c");
+ parameters.set("param2", "p2");
+ parameters.set("param3", "p3");
+
+
+ PageProvider provider = new PageProvider(MockPage.class, parameters);
+ provider.setPageSource(context);
+ IRequestHandler handler = new BookmarkablePageRequestHandler(provider);
+ Url url = optionPlaceholderEncoder.mapHandler(handler);
+ assertEquals("some/path/p2/p3/i1/i2?a=b&b=c", url.toString());
+ }
}
Modified: wicket/trunk/wicket-request/src/main/java/org/apache/wicket/request/mapper/AbstractMapper.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-request/src/main/java/org/apache/wicket/request/mapper/AbstractMapper.java?rev=1098550&r1=1098549&r2=1098550&view=diff
==============================================================================
--- wicket/trunk/wicket-request/src/main/java/org/apache/wicket/request/mapper/AbstractMapper.java (original)
+++ wicket/trunk/wicket-request/src/main/java/org/apache/wicket/request/mapper/AbstractMapper.java Mon May 2 13:15:49 2011
@@ -39,7 +39,33 @@ public abstract class AbstractMapper imp
*/
protected static String getPlaceholder(final String s)
{
- if ((s == null) || (s.length() < 4) || !s.startsWith("${") || !s.endsWith("}"))
+ return getPlaceholder(s, '$');
+ }
+
+ /**
+ * If the string is in an optional parameter placeholder format #{key} this method returns the
+ * key.
+ *
+ * @param s
+ * @return placeholder key or <code>null</code> if string is not in right format
+ */
+ protected static String getOptionalPlaceholder(final String s)
+ {
+ return getPlaceholder(s, '#');
+ }
+
+ /**
+ * If the string is in a placeholder format x{key}, where 'x' can be specified, this method
+ * returns the key.
+ *
+ * @param s
+ * @param startChar
+ * the character used to indicate the start of the placeholder
+ * @return placeholder key or <code>null</code> if string is not in right format
+ */
+ protected static String getPlaceholder(final String s, char startChar)
+ {
+ if ((s == null) || (s.length() < 4) || !s.startsWith(startChar + "{") || !s.endsWith("}"))
{
return null;
}