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 2010/04/19 15:20:32 UTC
svn commit: r935554 - in /tapestry/tapestry5/trunk/tapestry-core/src:
main/java/org/apache/tapestry5/internal/services/
main/java/org/apache/tapestry5/services/
test/java/org/apache/tapestry5/internal/services/
Author: hlship
Date: Mon Apr 19 13:20:32 2010
New Revision: 935554
URL: http://svn.apache.org/viewvc?rev=935554&view=rev
Log:
Change ComponentClassResolver service to expose the library mappings it creates.
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentClassResolverImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentClassResolver.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LibraryMapping.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentClassResolverImplTest.java
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentClassResolverImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentClassResolverImpl.java?rev=935554&r1=935553&r2=935554&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentClassResolverImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentClassResolverImpl.java Mon Apr 19 13:20:32 2010
@@ -44,7 +44,7 @@ public class ComponentClassResolverImpl
private final ClassNameLocator classNameLocator;
private final String appRootPackage;
-
+
private final String startPageName;
// Map from folder name to a list of root package names.
@@ -105,7 +105,7 @@ public class ComponentClassResolverImpl
@Inject
@Symbol(InternalConstants.TAPESTRY_APP_PACKAGE_PARAM)
String appRootPackage,
-
+
@Inject
@Symbol(SymbolConstants.START_PAGE_NAME)
String startPageName,
@@ -578,4 +578,88 @@ public class ComponentClassResolverImpl
}
});
}
+
+ public Map<String, String> getFolderToPackageMapping()
+ {
+ Map<String, String> result = CollectionFactory.newCaseInsensitiveMap();
+
+ for (String folder : mappings.keySet())
+ {
+ List<String> packageNames = mappings.get(folder);
+
+ String packageName = findCommonPackageNameForFolder(folder, packageNames);
+
+ result.put(folder, packageName);
+ }
+
+ return result;
+ }
+
+ static String findCommonPackageNameForFolder(String folder, List<String> packageNames)
+ {
+ String packageName = findCommonPackageName(packageNames);
+
+ if (packageName == null)
+ throw new RuntimeException(
+ String
+ .format(
+ "Package names for library folder '%s' (%s) can not be reduced to a common base package (of at least two terms).",
+ folder, InternalUtils.joinSorted(packageNames)));
+ return packageName;
+ }
+
+ static String findCommonPackageName(List<String> packageNames)
+ {
+ // BTW, this is what reduce is for in Clojure ...
+
+ String commonPackageName = packageNames.get(0);
+
+ for (int i = 1; i < packageNames.size(); i++)
+ {
+ commonPackageName = findCommonPackageName(commonPackageName, packageNames.get(i));
+
+ if (commonPackageName == null)
+ break;
+ }
+
+ return commonPackageName;
+ }
+
+ static String findCommonPackageName(String commonPackageName, String packageName)
+ {
+ String[] commonExploded = explode(commonPackageName);
+ String[] exploded = explode(packageName);
+
+ int count = Math.min(commonExploded.length, exploded.length);
+
+ int commonLength = 0;
+ int commonTerms = 0;
+
+ for (int i = 0; i < count; i++)
+ {
+ if (exploded[i].equals(commonExploded[i]))
+ {
+ // Keep track of the number of shared characters (including the dot seperators)
+
+ commonLength += exploded[i].length() + (i == 0 ? 0 : 1);
+ commonTerms++;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if (commonTerms < 2)
+ return null;
+
+ return commonPackageName.substring(0, commonLength);
+ }
+
+ private static final Pattern DOT = Pattern.compile("\\.");
+
+ private static String[] explode(String packageName)
+ {
+ return DOT.split(packageName);
+ }
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentClassResolver.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentClassResolver.java?rev=935554&r1=935553&r2=935554&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentClassResolver.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentClassResolver.java Mon Apr 19 13:20:32 2010
@@ -1,10 +1,10 @@
-// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2010 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// 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,
@@ -14,9 +14,12 @@
package org.apache.tapestry5.services;
-import org.apache.tapestry5.ioc.services.ClassNameLocator;
-
import java.util.List;
+import java.util.Map;
+
+import org.apache.tapestry5.Asset;
+import org.apache.tapestry5.ioc.annotations.UsesConfiguration;
+import org.apache.tapestry5.ioc.services.ClassNameLocator;
/**
* Resolves page names and component types to fully qualified class names. Pages and components may be provided by the
@@ -31,68 +34,91 @@ import java.util.List;
* maps from logical page name, component type, or mixin type to fully qualified class name.
* <p/>
* Certain ambiguities occur if mapped packages overlap, either in terms of the the prefixes or the package names. Keep
- * things clearly seperate to avoid lookup problems.
+ * things clearly separate to avoid lookup problems.
*/
+@UsesConfiguration(LibraryMapping.class)
public interface ComponentClassResolver
{
/**
* Converts a logical page name (such as might be encoded into a URL) into a fully qualified class name. The case of
* the page name is irrelevant.
- *
- * @param pageName page name
+ *
+ * @param pageName
+ * page name
* @return fully qualified class name for the page
- * @throws IllegalArgumentException if the name does not match a known page class
+ * @throws IllegalArgumentException
+ * if the name does not match a known page class
*/
String resolvePageNameToClassName(String pageName);
/**
* For a particular path, determines if the path is a logical page name. The check is case insensitive.
- *
- * @param pageName potential logical page name
+ *
+ * @param pageName
+ * potential logical page name
* @return true if the page name is valid
*/
boolean isPageName(String pageName);
/**
- * Returns a list of all page names, in sorted order.
+ * Returns a list of all page names, in sorted order.
*/
List<String> getPageNames();
/**
* Converts a fully qualified page class name into a page name (often, for inclusion as part of the URI). This value
* may later be passed to {@link #resolvePageNameToClassName(String)}.
- *
- * @param pageClassName fully qualified name of a page class
+ *
+ * @param pageClassName
+ * fully qualified name of a page class
* @return equivalent logical page name
- * @throws IllegalArgumentException if the name can not be resolved
+ * @throws IllegalArgumentException
+ * if the name can not be resolved
*/
String resolvePageClassNameToPageName(String pageClassName);
/**
* Returns the canonical form of a page name. The canonical form uses character case matching the underlying class
* name.
- *
- * @throws IllegalArgumentException if the page name does not match a logical page name
+ *
+ * @throws IllegalArgumentException
+ * if the page name does not match a logical page name
*/
String canonicalizePageName(String pageName);
/**
* Converts a component type (a logical component name such as might be used inside a template or annotation) into a
* fully qualified class name. Case is ignored in resolving the name.
- *
- * @param componentType a logical component type
+ *
+ * @param componentType
+ * a logical component type
* @return fully qualified class name
- * @throws IllegalArgumentException if the component type can not be resolved
+ * @throws IllegalArgumentException
+ * if the component type can not be resolved
*/
String resolveComponentTypeToClassName(String componentType);
/**
* Converts a logical mixin type (as with component types) into a fully qualified class name. Case is ignored when
* resolving the name.
- *
- * @param mixinType a logical mixin type
+ *
+ * @param mixinType
+ * a logical mixin type
* @return fully qualified class name
- * @throws IllegalArgumentException if the mixin type can not be resolved
+ * @throws IllegalArgumentException
+ * if the mixin type can not be resolved
*/
String resolveMixinTypeToClassName(String mixinType);
+
+ /**
+ * A mapping from virtual folder name to a package name (used for converting classpath {@link Asset}s
+ * to client URLs). This is derived from the contributed {@link LibraryMapping}s.
+ * <p>
+ * It is allowed to contribute multiple root packages as a single folder name. In this case, the best common package
+ * name is used. For example, if both <code>com.example.main</code> and <code>com.example.extras</code> is mapped to
+ * folder "example", then the package mapping for "example" will be <code>com.example</code>.
+ *
+ * @since 5.2.0
+ */
+ Map<String, String> getFolderToPackageMapping();
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LibraryMapping.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LibraryMapping.java?rev=935554&r1=935553&r2=935554&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LibraryMapping.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LibraryMapping.java Mon Apr 19 13:20:32 2010
@@ -4,7 +4,7 @@
// 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
+// 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,
@@ -17,13 +17,21 @@ package org.apache.tapestry5.services;
/**
* Used to configure the {@link ComponentClassResolver}, to allow it to map prefixes to library root packages (the
* application namespace is a special case of this). In each case, a prefix on the path is mapped to a package. Prefixes
- * should start and end with characters, such as "core". It is allowed for a prefix to contain a slash, though it is not
+ * should start and end with characters, such as "corelib". It is allowed for a prefix to contain a slash, though it is not
* recommended.
* <p/>
- * The root package name should have a number of sub-packages: <dl> <dt>pages</dt> <dd>contains named pages</dd>
- * <dt>components</dt> <dd>contains components</dd> <dt>mixins</dt> <dd>contains component mixins</dd> <dt>base</dt>
- * <dd>contains base classes</dd> </dl>
- *
+ * The root package name should have a number of sub-packages:
+ * <dl>
+ * <dt>pages</dt>
+ * <dd>contains named pages</dd>
+ * <dt>components</dt>
+ * <dd>contains components</dd>
+ * <dt>mixins</dt>
+ * <dd>contains component mixins</dd>
+ * <dt>base</dt>
+ * <dd>contains base classes</dd>
+ * </dl>
+ *
* @see org.apache.tapestry5.services.TapestryModule#contributeComponentClassResolver(org.apache.tapestry5.ioc.Configuration)
*/
public final class LibraryMapping
Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentClassResolverImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentClassResolverImplTest.java?rev=935554&r1=935553&r2=935554&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentClassResolverImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentClassResolverImplTest.java Mon Apr 19 13:20:32 2010
@@ -15,6 +15,7 @@
package org.apache.tapestry5.internal.services;
import org.apache.tapestry5.internal.test.InternalBaseTestCase;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.services.ClassNameLocator;
import org.apache.tapestry5.ioc.util.UnknownValueException;
import org.apache.tapestry5.services.ComponentClassResolver;
@@ -41,7 +42,6 @@ public class ComponentClassResolverImplT
private static final String LIB_ROOT_PACKAGE = "org.example.lib";
-
private ComponentClassResolverImpl create(Logger logger, ComponentInstantiatorSource source,
ClassNameLocator locator, LibraryMapping... mappings)
{
@@ -156,7 +156,7 @@ public class ComponentClassResolverImplT
verify();
}
-
+
@Test
public void canonicalize_start_page()
{
@@ -171,10 +171,11 @@ public class ComponentClassResolverImplT
train_locateComponentClassNames(locator, APP_ROOT_PACKAGE + ".pages", className);
replay();
-
+
List<LibraryMapping> mappings = Arrays.asList();
- ComponentClassResolver resolver = new ComponentClassResolverImpl(logger, source, locator, APP_ROOT_PACKAGE, "HomePage", mappings);
+ ComponentClassResolver resolver = new ComponentClassResolverImpl(logger, source, locator, APP_ROOT_PACKAGE,
+ "HomePage", mappings);
assertEquals(resolver.canonicalizePageName("HomePage"), "HomePage");
assertEquals(resolver.canonicalizePageName(""), "HomePage");
@@ -182,8 +183,6 @@ public class ComponentClassResolverImplT
verify();
}
-
-
@Test
public void page_name_in_subfolder()
@@ -926,4 +925,43 @@ public class ComponentClassResolverImplT
{
train_for_packages(source, APP_ROOT_PACKAGE);
}
+
+ @Test
+ public void common_package_name()
+ {
+ List<String> packageNames = CollectionFactory.newList("org.example.app.main", "org.example.app.sub");
+
+ assertEquals(ComponentClassResolverImpl.findCommonPackageName(packageNames), "org.example.app");
+ }
+
+ @Test
+ public void common_package_name_for_single_package()
+ {
+ List<String> packageNames = CollectionFactory.newList("org.example.app.main");
+
+ assertEquals(ComponentClassResolverImpl.findCommonPackageName(packageNames), "org.example.app.main");
+ }
+
+ @Test
+ public void expect_failure_when_no_common_package()
+ {
+ List<String> packageNames = CollectionFactory.newList("org.example.app.main", "org.demo.app.sub");
+
+ // "org" isn't good enough, we expect at least two terms.
+
+ try
+ {
+
+ ComponentClassResolverImpl.findCommonPackageNameForFolder("fred", packageNames);
+
+ unreachable();
+ }
+ catch (RuntimeException ex)
+ {
+ assertEquals(
+ ex.getMessage(),
+ "Package names for library folder 'fred' (org.demo.app.sub, org.example.app.main) can not be reduced to a common base package (of at least two terms).");
+ }
+
+ }
}