You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2006/10/11 05:40:10 UTC
svn commit: r462683 - in /tapestry/tapestry5/tapestry-core/trunk/src:
main/java/org/apache/tapestry/
main/java/org/apache/tapestry/internal/services/
main/java/org/apache/tapestry/internal/structure/
main/java/org/apache/tapestry/services/ main/java/or...
Author: hlship
Date: Tue Oct 10 20:40:09 2006
New Revision: 462683
URL: http://svn.apache.org/viewvc?view=rev&rev=462683
Log:
Start working on Link and LinkFactory.
Added:
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Link.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkImpl.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/LinkFactory.java
Modified:
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResources.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebRequestImpl.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebResponseImpl.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ComponentClassResolver.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/WebRequest.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/WebResponse.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/util/CollectionFactory.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/util/Defense.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/util/UtilMessages.java
tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties
tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/util/UtilStrings.properties
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/HelloWorld.java
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentClassResolverImplTest.java
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/util/DefenseTest.java
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResources.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResources.java?view=diff&rev=462683&r1=462682&r2=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResources.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ComponentResources.java Tue Oct 10 20:40:09 2006
@@ -35,12 +35,20 @@
String getId();
/**
- * Returns a string consisting of the name of the containing page, plus the concatinated ids of
- * all containing components, separated by periods. I.e., "com.foo.pages.MyPage:foo.bar.baz".
+ * Returns a string consisting of the class name of the containing page, and the
+ * {@link #getNestedId() nested id} of this component, separated by a colon. I.e.,
+ * "com.foo.pages.MyPage:foo.bar.baz". For a page, returns just the page class name.
*/
String getCompleteId();
+ /**
+ * Return a string consisting the concatinated ids of all containing components, separated by
+ * periods. I.e., "foo.bar.baz". Returns null for a page.
+ */
+
+ String getNestedId();
+
/** Returns the component model object that defines the behavior of the component. */
ComponentModel getComponentModel();
@@ -48,6 +56,13 @@
* Returns the component this object provides resources for.
*/
ComponentLifecycle getComponent();
+
+ /**
+ * Returns the page that contains this component. Technically, the page itself is an internal
+ * object in Tapestry and this returns the root component of the actual page, but from an
+ * application developer point of view, this is the page.
+ */
+ ComponentLifecycle getPage();
/**
* Returns an embedded component, given the component's id.
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Link.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Link.java?view=auto&rev=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Link.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Link.java Tue Oct 10 20:40:09 2006
@@ -0,0 +1,40 @@
+package org.apache.tapestry;
+
+import java.util.List;
+
+import org.apache.tapestry.services.Dispatcher;
+
+/**
+ * A link is the Tapestry representation of a URL or URI that triggers dynamic behavior. This link
+ * is in two parts: a path portion and a set of query parameters. A request for a link will
+ * ultimately be recognized by a {@link Dispatcher}.
+ * <p>
+ * Query parameter values are kept separate from the path portion to support encoding those values
+ * into hidden form fields (where appropriate).
+ *
+ * @author Howard M. Lewis Ship
+ */
+public interface Link
+{
+ /**
+ * The path portion of the URI. This will generally identify the the Tapestry page, event type,
+ * and component (within the page).
+ */
+ String getPath();
+
+ /**
+ * The names of any additional query parameters for the URI. Query parameters store less regular
+ * or less often used values that can not be expressed in the path. They also are used to store,
+ * or link to, persistent state.
+ *
+ * @return list of query parameter names, is alphabetical order
+ */
+ List<String> getParameterNames();
+
+ /**
+ * Returns the value of a specifically named query parameter, or null if no such query parameter
+ * is stored in the link.
+ */
+
+ String getParameterValue(String name);
+}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java?view=diff&rev=462683&r1=462682&r2=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassResolverImpl.java Tue Oct 10 20:40:09 2006
@@ -37,7 +37,7 @@
private final Map<String, List<String>> _mappings = newMap();
- // Cache of page names to page class names
+ // Cache of logical page names to page class names
private final Map<String, String> _pageClassCache = newThreadSafeMap();
@@ -45,6 +45,10 @@
private final Map<String, String> _componentClassCache = newThreadSafeMap();
+ // Cache of page class names back to logical page names
+
+ private final Map<String, String> _logicalPageNameCache = newThreadSafeMap();
+
public ComponentClassResolverImpl(ComponentInstantiatorSource componentInstantiatorSource,
Collection<LibraryMapping> mappings)
{
@@ -83,6 +87,7 @@
{
_pageClassCache.clear();
_componentClassCache.clear();
+ _logicalPageNameCache.clear();
}
public String resolvePageNameToClassName(String pageName)
@@ -193,6 +198,89 @@
private String buildClassName(String rootPackage, String subpackage, String extension)
{
return rootPackage + "." + subpackage + "." + extension;
+ }
+
+ public String resolvePageClassNameToPageName(String pageClassName)
+ {
+ String result = _logicalPageNameCache.get(pageClassName);
+
+ if (result == null)
+ {
+ result = findLogicalPageNameForClassName(pageClassName);
+ _logicalPageNameCache.put(pageClassName, result);
+ }
+
+ return result;
+ }
+
+ private String findLogicalPageNameForClassName(String pageClassName)
+ {
+ String bestName = null;
+
+ for (Map.Entry<String, List<String>> entry : _mappings.entrySet())
+ {
+ String prefix = entry.getKey();
+
+ for (String packageName : entry.getValue())
+ {
+ String potential = convertToLogicalPageName(pageClassName, packageName, prefix);
+
+ if (potential == null)
+ continue;
+
+ if (bestName == null)
+ {
+ bestName = potential;
+ continue;
+ }
+
+ // Generally, for there to be more than one match, something borderline illegal
+ // must be going on.
+
+ if (potential.length() < bestName.length())
+ bestName = potential;
+ }
+ }
+
+ if (bestName == null)
+ bestName = convertToLogicalPageName(pageClassName, _appRootPackage, null);
+
+ if (bestName == null)
+ throw new IllegalArgumentException(ServicesMessages.pageNameUnresolved(pageClassName));
+
+ return bestName;
+ }
+
+ private String convertToLogicalPageName(String pageClassName, String packageName, String prefix)
+ {
+ if (!pageClassName.startsWith(packageName))
+ return null;
+
+ String afterPackage = pageClassName.substring(packageName.length() + 1);
+ String[] names = afterPackage.split("\\.");
+
+ // The package name is the *root* package; we ignore it, but build up the rest
+ // of the logical page name from any intermediate folders/packages, leading
+ // up to the actual class name. If packages are nested in an ambiguous way,
+ // we may find something that looks like a mapping, but isn't.
+
+ if (!names[0].equals("pages"))
+ return null;
+
+ StringBuilder builder = new StringBuilder();
+
+ if (prefix != null)
+ builder.append(prefix);
+
+ for (int i = 1; i < names.length; i++)
+ {
+ if (builder.length() > 0)
+ builder.append("/");
+
+ builder.append(names[i]);
+ }
+
+ return builder.toString();
}
public void setApplicationPackage(String packageName)
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java?view=auto&rev=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java Tue Oct 10 20:40:09 2006
@@ -0,0 +1,73 @@
+package org.apache.tapestry.internal.services;
+
+import static org.apache.tapestry.util.Defense.cast;
+
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.Link;
+import org.apache.tapestry.internal.annotations.SuppressNullCheck;
+import org.apache.tapestry.runtime.ComponentLifecycle;
+import org.apache.tapestry.services.ComponentClassResolver;
+import org.apache.tapestry.services.LinkFactory;
+import org.apache.tapestry.services.WebRequest;
+import org.apache.tapestry.services.WebResponse;
+import org.apache.tapestry.util.Defense;
+
+/**
+ * @author Howard M. Lewis Ship
+ */
+@SuppressNullCheck
+public class LinkFactoryImpl implements LinkFactory
+{
+ private final WebRequest _request;
+
+ private final WebResponse _response;
+
+ private final ComponentClassResolver _componentClassResolver;
+
+ public LinkFactoryImpl(WebRequest request, WebResponse response,
+ ComponentClassResolver componentClassResolver)
+ {
+ _request = request;
+ _response = response;
+ _componentClassResolver = componentClassResolver;
+ }
+
+ public Link createActionLink(Object component, String action, boolean forForm,
+ Object... context)
+ {
+ ComponentLifecycle lifecycle = cast(component, ComponentLifecycle.class, "component");
+ Defense.notBlank(action, "action");
+
+ ComponentResources resources = lifecycle.getComponentResources();
+
+ ComponentLifecycle page = resources.getPage();
+
+ String logicalPageName = _componentClassResolver.resolvePageClassNameToPageName(page
+ .getClass().getName());
+
+ StringBuilder builder = new StringBuilder();
+
+ builder.append(_request.getContextPath());
+ builder.append("/");
+ builder.append(logicalPageName);
+ builder.append(".");
+ builder.append(action);
+ builder.append("/");
+ builder.append(resources.getNestedId());
+
+ for (Object id : context)
+ {
+ builder.append("/");
+
+ // TODO: Need to encode this for URLs? What if the string contains slashes, etc.?
+
+ builder.append(id.toString());
+ }
+
+ // TODO: Much more: query parameter for case where active page != component page.
+ // Letting listeners add extra parameters.
+
+ return new LinkImpl(_response, builder.toString());
+ }
+
+}
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkImpl.java?view=auto&rev=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkImpl.java Tue Oct 10 20:40:09 2006
@@ -0,0 +1,41 @@
+package org.apache.tapestry.internal.services;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.tapestry.Link;
+import org.apache.tapestry.services.WebResponse;
+
+/**
+ * Starting implementation of {@link Link}. Currently does not support query parameters.
+ *
+ * @author Howard M. Lewis Ship
+ */
+public class LinkImpl implements Link
+{
+ private final WebResponse _response;
+
+ private final String _path;
+
+ public LinkImpl(WebResponse response, String path)
+ {
+ _response = response;
+ _path = path;
+ }
+
+ public List<String> getParameterNames()
+ {
+ return Collections.emptyList();
+ }
+
+ public String getParameterValue(String name)
+ {
+ return null;
+ }
+
+ public String getPath()
+ {
+ return _response.encodeURL(_path);
+ }
+
+}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java?view=diff&rev=462683&r1=462682&r2=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java Tue Oct 10 20:40:09 2006
@@ -197,4 +197,9 @@
{
return MESSAGES.format("page-does-not-exist", pageName);
}
+
+ static String pageNameUnresolved(String pageClassName)
+ {
+ return MESSAGES.format("page-name-unresolved", pageClassName);
+ }
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebRequestImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebRequestImpl.java?view=diff&rev=462683&r1=462682&r2=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebRequestImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebRequestImpl.java Tue Oct 10 20:40:09 2006
@@ -56,4 +56,9 @@
return _request.getServletPath();
}
+ public String getContextPath()
+ {
+ return _request.getContextPath();
+ }
+
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebResponseImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebResponseImpl.java?view=diff&rev=462683&r1=462682&r2=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebResponseImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebResponseImpl.java Tue Oct 10 20:40:09 2006
@@ -38,4 +38,8 @@
return _response.getWriter();
}
+ public String encodeURL(String URL)
+ {
+ return _response.encodeURL(URL);
+ }
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java?view=diff&rev=462683&r1=462682&r2=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElement.java Tue Oct 10 20:40:09 2006
@@ -29,7 +29,7 @@
RenderCommand
{
/** Returns the page which contains this component. */
- Page getPage();
+ Page getContainingPage();
/**
* Containing component (or null for the root component of a page).
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java?view=diff&rev=462683&r1=462682&r2=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java Tue Oct 10 20:40:09 2006
@@ -17,7 +17,6 @@
import static org.apache.tapestry.util.CollectionFactory.newList;
import static org.apache.tapestry.util.CollectionFactory.newMap;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -62,7 +61,9 @@
private final String _id;
- private String _completeId;
+ private final String _nestedId;
+
+ private final String _completeId;
// For the momement, every component will have a template, even if it consists of
// just a page element to queue up a BeforeRenderBody phase.
@@ -99,6 +100,7 @@
TypeCoercer typeCoercer)
{
this(page, null, null, instantiator, model, typeCoercer, null);
+
}
/**
@@ -134,51 +136,36 @@
_typeCoercer = typeCoercer;
_component = instantiator.newInstance(this);
- }
- public String getCompleteId()
- {
- if (_completeId == null)
- buildCompleteId();
-
- return _completeId;
- }
+ String componentClassName = _component.getClass().getName();
- private void buildCompleteId()
- {
- List<String> ids = newList();
+ // A page (really, the root component of a page) does not have a container.
- ComponentPageElement cpe = this;
- while (true)
+ if (container == null)
{
- // Stop at the root component, which has no id.
- String id = cpe.getId();
-
- if (id == null)
- break;
-
- ids.add(id);
-
- cpe = cpe.getContainer();
+ _completeId = componentClassName;
+ _nestedId = null;
}
-
- String pageName = getPage().getName();
-
- Collections.reverse(ids);
-
- StringBuilder builder = new StringBuilder(pageName);
-
- int count = ids.size();
- for (int i = 0; i < count; i++)
+ else
{
- builder.append(i == 0 ? ':' : '.');
- builder.append(ids.get(i));
+ String containerId = _container.getNestedId();
+
+ _nestedId = containerId == null ? id : containerId + "." + id;
+ _completeId = componentClassName + ":" + _nestedId;
}
+ }
- _completeId = builder.toString();
+ public String getCompleteId()
+ {
+ return _completeId;
}
- public Page getPage()
+ public String getNestedId()
+ {
+ return _nestedId;
+ }
+
+ public Page getContainingPage()
{
return _page;
}
@@ -186,6 +173,11 @@
public ComponentPageElement getContainer()
{
return _container;
+ }
+
+ public ComponentLifecycle getPage()
+ {
+ return _page.getRootElement().getComponent();
}
public void addToBody(PageElement element)
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ComponentClassResolver.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ComponentClassResolver.java?view=diff&rev=462683&r1=462682&r2=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ComponentClassResolver.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/ComponentClassResolver.java Tue Oct 10 20:40:09 2006
@@ -25,11 +25,11 @@
* package is expected to have two sub-packages, "pages" and "components". Page names are searched
* for in the pages package, and components types are searched for the components package.
* <p>
- * When searching, the portion of the page name (or component type) before the slash is compared
- * with known prefixes in order to find the correct root package name. Thus when encountering the
- * page name (or component type) "foo/Bar", a search for a mapping for prefix "foo" will occur. If
- * "foo" is mapped, then the final class name will be built from foo's root package, then "pages"
- * (or "components"), then "Bar".
+ * When searching, the portion of the logical page name (or component type) before the slash is
+ * compared with known prefixes in order to find the correct root package name. Thus when
+ * encountering the page name (or component type) "foo/Bar", a search for a mapping for prefix "foo"
+ * will occur. If "foo" is mapped, then the final class name will be built from foo's root package,
+ * then "pages" (or "components"), then "Bar".
* <p>
* If "foo" is not mapped, then the name is resolved as part of the application. The final class
* name will be "<em>application-root</em>.pages.foo.Bar", meaning that the prefix is
@@ -61,7 +61,7 @@
public interface ComponentClassResolver
{
/**
- * Converts a partial page name (such as might be encoded into a URL) into a fully qualified
+ * Converts a logical page name (such as might be encoded into a URL) into a fully qualified
* class name.
*
* @param pageName
@@ -69,6 +69,18 @@
* @return fully qualified class name of null if the page name can not be resolved
*/
String resolvePageNameToClassName(String pageName);
+
+ /**
+ * Converts a fully qualified page class name into a logical class name (often, for inclusion in
+ * as part of the URI.
+ *
+ * @param pageClassName
+ * fully qualified name of a page class
+ * @return equivalent logical page name
+ * @throws IllegalArgumentException
+ * if the name can not be resolved
+ */
+ String resolvePageClassNameToPageName(String pageClassName);
/**
* Converts a component type (a partial component name such as might be used inside a template
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/LinkFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/LinkFactory.java?view=auto&rev=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/LinkFactory.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/LinkFactory.java Tue Oct 10 20:40:09 2006
@@ -0,0 +1,28 @@
+package org.apache.tapestry.services;
+
+import org.apache.tapestry.Link;
+
+/**
+ * A source for {@link Link} objects.
+ *
+ * @author Howard M. Lewis Ship
+ */
+public interface LinkFactory
+{
+ /**
+ * Creates a stateful action link. Action links are built for components. Action links are
+ * encoded by the current request (that is, bound to the current request's session, if any).
+ *
+ * @param component
+ * a component instance
+ * @param action
+ * a name associated with the action
+ * @param forForm
+ * true if the link is for a form, false otherwise
+ * @param context
+ * Additional path data, each value will be converted to a string and appended to the
+ * URI
+ * @return a link
+ */
+ Link createActionLink(Object component, String action, boolean forForm, Object... context);
+}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java?view=diff&rev=462683&r1=462682&r2=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java Tue Oct 10 20:40:09 2006
@@ -746,4 +746,13 @@
}
};
}
+
+ /** Service used to create links for components and pages. */
+
+ public LinkFactory buildLinkFactory(@InjectService("WebRequest")
+ WebRequest request, @InjectService("WebResponse")
+ WebResponse response)
+ {
+ return null;
+ }
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/WebRequest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/WebRequest.java?view=diff&rev=462683&r1=462682&r2=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/WebRequest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/WebRequest.java Tue Oct 10 20:40:09 2006
@@ -45,4 +45,12 @@
* @return
*/
String getPath();
+
+ /**
+ * Returns the context path portion of the URI. This always starts with a "/" character and does
+ * not end with one, with the exception of servlets in the root context, which return the empty
+ * string.
+ *
+ */
+ String getContextPath();
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/WebResponse.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/WebResponse.java?view=diff&rev=462683&r1=462682&r2=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/WebResponse.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/WebResponse.java Tue Oct 10 20:40:09 2006
@@ -29,4 +29,13 @@
* commit the output.
*/
PrintWriter getPrintWriter() throws IOException;
+
+ /**
+ * Encodes the URL, ensuring that a session id is included (if a session exists, and as
+ * necessary depending on the client browser's use of cookies).
+ *
+ * @param URL
+ * @return the same URL or a different one with additional information to track the user session
+ */
+ String encodeURL(String URL);
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/util/CollectionFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/util/CollectionFactory.java?view=diff&rev=462683&r1=462682&r2=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/util/CollectionFactory.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/util/CollectionFactory.java Tue Oct 10 20:40:09 2006
@@ -61,7 +61,7 @@
}
/** Contructs a new {@link HashSet} and initializes it using the provided collection. */
- public static <T> Set<T> newSet(Collection<T> values)
+ public static <T> Set<T> newSet(Collection<? extends T> values)
{
return new HashSet<T>(values);
}
@@ -69,7 +69,7 @@
/**
* Constructs a new {@link java.util.HashMap} instance by copying an existing Map instance.
*/
- public static <K, V> Map<K, V> newMap(Map<K, V> map)
+ public static <K, V> Map<K, V> newMap(Map<? extends K, ? extends V> map)
{
return new HashMap<K, V>(map);
}
@@ -94,8 +94,8 @@
return new LinkedList<T>();
}
- /** Constructs and returns a new {@link ArrayList} as a copy of the provided list. */
- public static <T> List<T> newList(Collection<T> list)
+ /** Constructs and returns a new {@link ArrayList} as a copy of the provided collection. */
+ public static <T> List<T> newList(Collection<? extends T> list)
{
return new ArrayList<T>(list);
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/util/Defense.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/util/Defense.java?view=diff&rev=462683&r1=462682&r2=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/util/Defense.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/util/Defense.java Tue Oct 10 20:40:09 2006
@@ -74,4 +74,28 @@
throw new IllegalArgumentException(UtilMessages.parameterWasBlank(parameterName));
}
+
+ /**
+ * Checks that the provided value is not null, and may be cast to the desired type.
+ *
+ * @param <T>
+ * @param parameterValue
+ * @param type
+ * @param parameterName
+ * @throws IllegalArgumentException
+ * if the value is null, or is not assignable to the indicated type
+ * @return the casted value
+ */
+ public static <T> T cast(Object parameterValue, Class<T> type, String parameterName)
+ {
+ notNull(parameterValue, parameterName);
+
+ if (!type.isInstance(parameterValue))
+ throw new IllegalArgumentException(UtilMessages.badCast(
+ parameterName,
+ parameterValue,
+ type));
+
+ return type.cast(parameterValue);
+ }
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/util/UtilMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/util/UtilMessages.java?view=diff&rev=462683&r1=462682&r2=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/util/UtilMessages.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/util/UtilMessages.java Tue Oct 10 20:40:09 2006
@@ -37,4 +37,9 @@
{
return MESSAGES.format("parameter-was-blank", parameterName);
}
+
+ static String badCast(String parameterName, Object parameterValue, Class type)
+ {
+ return MESSAGES.format("bad-cast", parameterName, parameterValue, type.getName());
+ }
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties?view=diff&rev=462683&r1=462682&r2=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties Tue Oct 10 20:40:09 2006
@@ -49,4 +49,5 @@
binding-source-failure=Could not convert '%s' into a component parameter binding: %s
no-coercion-found=Could not find a coercion from type %s to type %s. Available coercions: %s.
unable-to-resolve-component-type=Unable to resolve component '%s' to a component class name.
-page-does-not-exist=Page '%s' is not defined by this application.
\ No newline at end of file
+page-does-not-exist=Page '%s' is not defined by this application.
+page-name-unresolved=Unable to resolve class name %s to a logical page name.
\ No newline at end of file
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/util/UtilStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/util/UtilStrings.properties?view=diff&rev=462683&r1=462682&r2=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/util/UtilStrings.properties (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/util/UtilStrings.properties Tue Oct 10 20:40:09 2006
@@ -13,4 +13,5 @@
# limitations under the License.
parameter-was-null=Parameter %s was null.
-parameter-was-blank=Parameter %s was null or contained only whitespace.
\ No newline at end of file
+parameter-was-blank=Parameter %s was null or contained only whitespace.
+bad-cast=Parameter %s (%s) is not assignable to type %s.
\ No newline at end of file
Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/HelloWorld.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/HelloWorld.java?view=diff&rev=462683&r1=462682&r2=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/HelloWorld.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/components/HelloWorld.java Tue Oct 10 20:40:09 2006
@@ -24,7 +24,7 @@
@BeginRender
void renderMessage(MarkupWriter writer)
{
- writer.write("Bonjour from HelloWorld component.");
+ writer.write("Why wait? Make changes here and see them immediately!");
}
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentClassResolverImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentClassResolverImplTest.java?view=diff&rev=462683&r1=462682&r2=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentClassResolverImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentClassResolverImplTest.java Tue Oct 10 20:40:09 2006
@@ -69,6 +69,24 @@
}
@Test
+ public void class_name_to_simple_page_name()
+ {
+ ComponentInstantiatorSource source = newComponentInstantiatorSource();
+
+ train_for_app_packages(source);
+
+ replay();
+
+ ComponentClassResolver resolver = create(source);
+
+ assertEquals(
+ resolver.resolvePageClassNameToPageName(APP_ROOT_PACKAGE + ".pages.SimplePage"),
+ "SimplePage");
+
+ verify();
+ }
+
+ @Test
public void resolved_page_names_are_cached()
{
ComponentInstantiatorSource source = newComponentInstantiatorSource();
@@ -131,6 +149,52 @@
}
@Test
+ public void page_class_name_resolved_to_core_page()
+ {
+ ComponentInstantiatorSource source = newComponentInstantiatorSource();
+
+ train_for_packages(source, CORE_ROOT_PACKAGE);
+ train_for_app_packages(source);
+
+ replay();
+
+ ComponentClassResolver resolver = create(source, new LibraryMapping(CORE_PREFIX,
+ CORE_ROOT_PACKAGE));
+
+ assertEquals(
+ resolver.resolvePageClassNameToPageName(CORE_ROOT_PACKAGE + ".pages.CorePage"),
+ "core/CorePage");
+
+ verify();
+ }
+
+ @Test
+ public void resolved_logical_page_names_are_cached()
+ {
+ ComponentInstantiatorSource source = newComponentInstantiatorSource();
+
+ train_for_packages(source, CORE_ROOT_PACKAGE);
+ train_for_app_packages(source);
+
+ replay();
+
+ ComponentClassResolver resolver = create(source, new LibraryMapping(CORE_PREFIX,
+ CORE_ROOT_PACKAGE));
+
+ String className = CORE_ROOT_PACKAGE + ".pages.CorePage";
+
+ String logicalName1 = resolver.resolvePageClassNameToPageName(className);
+ String logicalName2 = resolver.resolvePageClassNameToPageName(className);
+
+ // Given that the value is computed on the fly, if its the same (not just equal, but the
+ // same), that proves that the value was cached.
+
+ assertSame(logicalName2, logicalName1);
+
+ verify();
+ }
+
+ @Test
public void page_found_in_library()
{
ComponentInstantiatorSource source = newComponentInstantiatorSource();
@@ -148,6 +212,84 @@
assertEquals(resolver.resolvePageNameToClassName("lib/LibPage"), LIB_ROOT_PACKAGE
+ ".pages.LibPage");
+
+ verify();
+ }
+
+ @Test
+ public void class_name_resolves_to_folder_under_library()
+ {
+ ComponentInstantiatorSource source = newComponentInstantiatorSource();
+
+ train_for_packages(source, LIB_ROOT_PACKAGE);
+ train_for_packages(source, CORE_ROOT_PACKAGE);
+ train_for_app_packages(source);
+
+ replay();
+
+ ComponentClassResolver resolver = create(source, new LibraryMapping(LIB_PREFIX,
+ LIB_ROOT_PACKAGE), new LibraryMapping(CORE_PREFIX, CORE_ROOT_PACKAGE));
+
+ assertEquals(resolver.resolvePageClassNameToPageName(LIB_ROOT_PACKAGE
+ + ".pages.foo.bar.LibPage"), LIB_PREFIX + "/foo/bar/LibPage");
+
+ verify();
+ }
+
+ @Test
+ public void class_name_does_not_resolve_to_page_name()
+ {
+ ComponentInstantiatorSource source = newComponentInstantiatorSource();
+
+ train_for_packages(source, CORE_ROOT_PACKAGE);
+ train_for_app_packages(source);
+
+ replay();
+
+ ComponentClassResolver resolver = create(source, new LibraryMapping(CORE_PREFIX,
+ CORE_ROOT_PACKAGE));
+
+ String className = LIB_ROOT_PACKAGE + ".pages.LibPage";
+
+ try
+ {
+ resolver.resolvePageClassNameToPageName(className);
+ unreachable();
+ }
+ catch (IllegalArgumentException ex)
+ {
+ assertEquals(ex.getMessage(), "Unable to resolve class name " + className
+ + " to a logical page name.");
+ }
+
+ verify();
+ }
+
+ @Test
+ public void class_name_not_in_a_pages_package()
+ {
+ ComponentInstantiatorSource source = newComponentInstantiatorSource();
+
+ train_for_packages(source, CORE_ROOT_PACKAGE);
+ train_for_app_packages(source);
+
+ replay();
+
+ ComponentClassResolver resolver = create(source, new LibraryMapping(CORE_PREFIX,
+ CORE_ROOT_PACKAGE));
+
+ String className = CORE_ROOT_PACKAGE + ".foo.CorePage";
+
+ try
+ {
+ resolver.resolvePageClassNameToPageName(className);
+ unreachable();
+ }
+ catch (IllegalArgumentException ex)
+ {
+ assertEquals(ex.getMessage(), "Unable to resolve class name " + className
+ + " to a logical page name.");
+ }
verify();
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java?view=diff&rev=462683&r1=462682&r2=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/ComponentPageElementImplTest.java Tue Oct 10 20:40:09 2006
@@ -115,6 +115,8 @@
ParameterModel pmodel = newParameterModel();
Location l = newLocation();
+ train_getNestedId(container, null);
+
train_getParameterNames(model, "wilma", "barney", "fred");
train_getParameterModel(model, "wilma", pmodel);
train_isRequired(pmodel, true);
@@ -147,6 +149,12 @@
verify();
}
+ protected void train_getNestedId(ComponentPageElement container, String nestedId)
+ {
+ container.getNestedId();
+ setReturnValue(nestedId);
+ }
+
@Test
public void is_invariant()
{
@@ -239,8 +247,6 @@
Instantiator ins = newInstantiator(component);
ComponentModel model = newComponentModel();
- train_getName(page, "foo.Bar");
-
replay();
ComponentPageElement cpe = new ComponentPageElementImpl(page, ins, model, null);
@@ -252,10 +258,11 @@
}
catch (IllegalArgumentException ex)
{
- assertEquals(
- ex.getMessage(),
- "Component foo.Bar does not contain an embedded component with id 'unknown'.");
+ assertEquals(ex.getMessage(), "Component " + component.getClass().getName()
+ + " does not contain an embedded component with id 'unknown'.");
}
+
+ verify();
}
@Test
Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/util/DefenseTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/util/DefenseTest.java?view=diff&rev=462683&r1=462682&r2=462683
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/util/DefenseTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/util/DefenseTest.java Tue Oct 10 20:40:09 2006
@@ -14,6 +14,7 @@
package org.apache.tapestry.util;
+import static org.apache.tapestry.util.Defense.cast;
import static org.apache.tapestry.util.Defense.notBlank;
import static org.apache.tapestry.util.Defense.notNull;
@@ -85,5 +86,47 @@
public void non_blank_parameter_is_valid()
{
assertEquals("fred", notBlank(" fred\n", "biff"));
+ }
+
+ @Test
+ public void cast_is_also_not_null()
+ {
+ try
+ {
+ cast(null, String.class, "fred");
+ unreachable();
+ }
+ catch (IllegalArgumentException ex)
+ {
+ assertEquals(ex.getMessage(), "Parameter fred was null.");
+ }
+ }
+
+ @Test
+ public void succesful_cast()
+ {
+ StringBuffer b = new StringBuffer();
+
+ Appendable a = cast(b, Appendable.class, "fred");
+
+ assertSame(a, b);
+ }
+
+ @Test
+ public void bad_cast()
+ {
+ StringBuffer b = new StringBuffer("fred-value");
+
+ try
+ {
+ cast(b, String.class, "fred");
+ unreachable();
+ }
+ catch (IllegalArgumentException ex)
+ {
+ assertEquals(
+ ex.getMessage(),
+ "Parameter fred (fred-value) is not assignable to type java.lang.String.");
+ }
}
}