No, I do not have commit rights.  I have created an issue on JIRA and
attached the project with the classes added.
http://wicketstuff.org/jira/browse/WSMINIS-8

Regards,
Matthew.

On Tue, Dec 16, 2008 at 4:08 PM, Jeremy Thomerson <jer...@wickettraining.com
> wrote:

> Do you have commit rights to wicketstuff?  If you do, just commit it.  If
> not, you could ask for it or create a patch on the JIRA:
> http://wicketstuff.org/jira/secure/BrowseProject.jspa?id=10020
>
> If you need help, let us know.
>
>
>
> On Tue, Dec 16, 2008 at 3:37 PM, Matthew Hanlon <mrhan...@gmail.com>
> wrote:
>
> > I just realized that gmail wasn't replying to the list.  Sorry.  For
> > posterity...
> >
> > On Tue, Dec 16, 2008 at 3:34 PM, Matthew Hanlon <mrhan...@gmail.com>
> > wrote:
> >
> > > Below is the updated CaseInsensitiveClassResolver I implemented per
> your
> > > suggestions.  It uses MatchingResources from wicketstuff annotation.
>  It
> > > scans packages on-demand and caches the results for future lookup.  It
> > seems
> > > fairly fast, and due to the cache it doesn't have to re-scan the
> > classpath.
> > >  Also below is my CaseInsensitivePackageRequestTargetUrlCodingStrategy
> > > (Gasp!  What an awfully long name.).
> > > If you think it's worthwhile, I'd love to contribute it to
> > > wicketstuff-minis.  How would one got about doing that?
> > >
> > > public class CaseInsensitiveClassResolver implements IClassResolver {
> > >
> > > private static final Logger logger =
> > > LoggerFactory.getLogger(CaseInsensitiveClassResolver.class);
> > >  private DefaultClassResolver resolver = new DefaultClassResolver();
> > >  public Iterator<URL> getResources(String name) {
> > >  return resolver.getResources(name);
> > > }
> > >  private Map<String, Map<String, Class<?>>> cache = new HashMap<String,
> > > Map<String,Class<?>>>();
> > >  public Class<?> resolveClass(String classname) throws
> > > ClassNotFoundException {
> > > Class<?> clazz = null;
> > >  try {
> > > clazz = resolver.resolveClass(classname);
> > > } catch (ClassNotFoundException e1) {
> > >  clazz = resolveClassCaseInsensitive(classname);
> > > } catch (NoClassDefFoundError e2) {
> > >  clazz = resolveClassCaseInsensitive(classname);
> > > }
> > > if (clazz == null) {
> > >  throw new ClassNotFoundException("Unable to resolve class for name " +
> > > classname);
> > > }
> > >  return clazz;
> > > }
> > >  public Class<?> resolveClassCaseInsensitive(String classname) {
> > > if (logger.isDebugEnabled()) {
> > >  logger.debug("Class not found for " + classname + ".  Trying to look
> up
> > > case-insensitive.");
> > > }
> > >  String packageName = classname.substring(0,
> classname.lastIndexOf('.'));
> > >  if (! cache.containsKey(packageName)) {
> > > cache.put(packageName, scan(getPatternForPackage(packageName)));
> > >  }
> > >  return cache.get(packageName).get(classname.toLowerCase());
> > >  }
> > >  /**
> > >      * Get the Spring search pattern given a package name or part of a
> > > package name
> > >      * @param packageName a package name
> > >      * @return a Spring search pattern for the given package
> > >      */
> > >     public String getPatternForPackage(String packageName)
> > >     {
> > >         if (packageName == null) packageName = "";
> > >         packageName = packageName.replace('.', '/');
> > >         if (!packageName.endsWith("/"))
> > >         {
> > >             packageName += '/';
> > >         }
> > >
> > >         return "classpath*:" + packageName + "**/*.class";
> > >     }
> > >
> > >     /**
> > >      *
> > >      * @param pattern
> > >      */
> > >     private Map<String, Class<?>> scan(final String pattern) {
> > >      Map<String, Class<?>> classMap = new HashMap<String, Class<?>>();
> > >  MatchingResources resources = new MatchingResources(pattern);
> > >         MetadataReaderFactory f = new SimpleMetadataReaderFactory();
> > > for (Resource r : resources.getAllMatches()) {
> > >  MetadataReader meta = null;
> > >             try
> > >             {
> > >                 meta = f.getMetadataReader(r);
> > >             }
> > >             catch (IOException e)
> > >             {
> > >                 throw new RuntimeException("Unable to get
> MetadataReader
> > > for " + r, e);
> > >             }
> > >             try {
> > >             ClassMetadata cmd = meta.getClassMetadata();
> > >             String classname = cmd.getClassName();
> > >             try {
> > >              classMap.put(classname.toLowerCase(),
> > > getClass().getClassLoader().loadClass(classname));
> > >  } catch (ClassNotFoundException e) {
> > > logger.error("Error loading class for name " + classname);
> > >  }
> > >             } catch (Throwable e) {
> > > logger.error("Unknown Error.", e);
> > >             }
> > > }
> > > return classMap;
> > > }
> > >
> > > }
> > >
> > > public class CaseInsensitivePackageRequestTargetUrlCodingStrategy
> extends
> > > PackageRequestTargetUrlCodingStrategy {
> > >
> > > private static final Logger log =
> > >
> >
> LoggerFactory.getLogger(CaseInsensitivePackageRequestTargetUrlCodingStrategy.class);
> > >  public CaseInsensitivePackageRequestTargetUrlCodingStrategy(final
> String
> > > path, PackageName packageName) {
> > > super(path, packageName);
> > >  this.packageName = packageName;
> > > }
> > >
> > > /** package for this mount. */
> > >  private final PackageName packageName;
> > >  private IClassResolver resolver = new CaseInsensitiveClassResolver();
> > >  /**
> > >  * @see
> > >
> >
> org.apache.wicket.request.target.coding.IRequestTargetUrlCodingStrategy#decode(org.apache.wicket.request.RequestParameters)
> > >  */
> > > public IRequestTarget decode(RequestParameters requestParameters)
> > > {
> > >  String remainder =
> > > requestParameters.getPath().substring(getMountPath().length());
> > > final String parametersFragment;
> > >  int ix = remainder.indexOf('/', 1);
> > > if (ix == -1)
> > > {
> > >  ix = remainder.length();
> > > parametersFragment = "";
> > > }
> > >  else
> > > {
> > > parametersFragment = remainder.substring(ix);
> > >  }
> > >
> > > if (remainder.startsWith("/"))
> > > {
> > >  remainder = remainder.substring(1);
> > > ix--;
> > > }
> > >  else
> > > {
> > > // There is nothing after the mount path!
> > >  return null;
> > > }
> > >
> > > final String bookmarkablePageClassName = packageName + "." +
> > > remainder.substring(0, ix);
> > >  Class bookmarkablePageClass;
> > > try
> > > {
> > >  bookmarkablePageClass =
> > resolver.resolveClass(bookmarkablePageClassName);
> > > }
> > >  catch (Exception e)
> > > {
> > > log.debug(e.getMessage());
> > >  return null;
> > > }
> > > PageParameters parameters = new
> > > PageParameters(decodeParameters(parametersFragment,
> > >  requestParameters.getParameters()));
> > >
> > > String pageMapName =
> > > (String)parameters.remove(WebRequestCodingStrategy.PAGEMAP);
> > >  pageMapName = WebRequestCodingStrategy.decodePageMapName(pageMapName);
> > > requestParameters.setPageMapName(pageMapName);
> > >
> > > // do some extra work for checking whether this is a normal request to
> a
> > > // bookmarkable page, or a request to a stateless page (in which case a
> > >  // wicket:interface parameter should be available
> > > final String interfaceParameter = (String)parameters
> > >  .remove(WebRequestCodingStrategy.INTERFACE_PARAMETER_NAME);
> > >
> > > if (interfaceParameter != null)
> > >  {
> > > WebRequestCodingStrategy.addInterfaceParameters(interfaceParameter,
> > > requestParameters);
> > >  return new BookmarkableListenerInterfaceRequestTarget(pageMapName,
> > > bookmarkablePageClass, parameters,
> requestParameters.getComponentPath(),
> > >  requestParameters.getInterfaceName(),
> > > requestParameters.getVersionNumber());
> > > }
> > >  else
> > > {
> > > return new BookmarkablePageRequestTarget(pageMapName,
> > > bookmarkablePageClass, parameters);
> > >  }
> > > }
> > > }
> > >
> > >
> > >
> > > On Fri, Dec 12, 2008 at 10:57 AM, Jeremy Thomerson <
> > > jer...@wickettraining.com> wrote:
> > >
> > >> One other thing - I think that the contract of IClassResolver would
> mean
> > >> that rather than returning null, you throw a ClassNotFoundException.
> > >>
> > >>
> > >> On Fri, Dec 12, 2008 at 8:24 AM, Matthew Hanlon <mrhan...@gmail.com
> > >wrote:
> > >>
> > >>> Great ideas, thanks for the input.  I agree on all points.  My
> initial
> > >>> implementation is certainly the naive approach, basically a proof of
> > >>> concept.  I'll look into what you mention in 4 and let you know what
> I
> > find.
> > >>>
> > >>>
> > >>>
> > >>> On Fri, Dec 12, 2008 at 12:44 AM, Jeremy Thomerson <
> > >>> jer...@wickettraining.com> wrote:
> > >>>
> > >>>> Sounds like an interesting idea.  Here are a few thoughts I had
> after
> > >>>> seeing it.  Hopefully these are helpful.
> > >>>>
> > >>>> 1 - Say you had a page "CustomerAdminLoginPage" - this yields
> > 4,194,304 combinations!
> > >>>> Cache the result - either the class you found or the fact that you
> > could not
> > >>>> find a class.  (you will have 2 to the nth power, where n equals the
> > length
> > >>>> of the simple name)
> > >>>> 2 - DON'T use StringBuilder just to split it later - that's not what
> > >>>> it's for!  It's very slow and is constantly resizing it's internal
> > array.
> > >>>> You could use something like the code I pasted below to use a single
> > array,
> > >>>> initialized ahead of time to the proper size.
> > >>>>
> > >>>> 3 - I would suggest not even holding an array of possible combos -
> > >>>> longer class names take a ton of memory because of all the millions
> of
> > >>>> strings created.  If you must go through all combos to try to find a
> > match,
> > >>>> just look for the match in your loop rather than looping to create
> an
> > array
> > >>>> of combos and then re-looping to try to find a match.
> > >>>>
> > >>>> 4 - Now - I would suggest seeing if you can avoid looping through
> all
> > >>>> possible combos altogether.  Look at how Wicket Annotations (in
> Wicket
> > >>>> Stuff) does classpath scanning...  I would think that this would be
> > much
> > >>>> more efficient - scan the package ahead of time and find all classes
> > in the
> > >>>> package and cache their names.  Then just do a case-insensitive look
> > into
> > >>>> your cache - this saves you all the memory and processing trouble of
> > ever
> > >>>> computing all the combos and trying to load potentially millions of
> > >>>> non-existent classes.
> > >>>>
> > >>>> 5 - If you get this to work and work well, add it to
> wicketstuff-minis
> > >>>> or a similar project where others can easily use it - let me know if
> > you
> > >>>> need help accomplishing that.
> > >>>>
> > >>>>
> > >>>> Here's an example of an improved method of finding the combos -
> > probably
> > >>>> could still be improved considerably, but this is a significant
> > improvement
> > >>>> over your first rough draft.  (Although see point 4 - I recommend
> not
> > even
> > >>>> using this method at all)
> > >>>>
> > >>>>     private static int capsCombinations(String[] combos, String
> word,
> > >>>> int startIndex, int arrayIndex) {
> > >>>>         if (arrayIndex == 0) {
> > >>>>             word = word.toLowerCase();
> > >>>>             combos[arrayIndex++] = word;
> > >>>>         }
> > >>>>         if (arrayIndex == combos.length) {
> > >>>>             return arrayIndex;
> > >>>>         } else {
> > >>>>             while (startIndex < word.length()) {
> > >>>>                 char[] chars = word.toCharArray();
> > >>>>                 chars[startIndex] =
> > >>>> Character.toUpperCase(chars[startIndex]);
> > >>>>                 String string = String.valueOf(chars);
> > >>>>                 combos[arrayIndex++] = string;
> > >>>>                 arrayIndex = capsCombinations(combos, string,
> > >>>> ++startIndex, arrayIndex);
> > >>>>             }
> > >>>>             return arrayIndex;
> > >>>>         }
> > >>>>     }
> > >>>>
> > >>>>     public static void main(String[] args) throws Exception {
> > >>>>         long start = System.currentTimeMillis();
> > >>>>         String name = "CustomerAdminLoginPage";
> > >>>>         String[] combos = new String[(int) Math.pow(2,
> > name.length())];
> > >>>>         capsCombinations(combos, name, 0, 0);
> > >>>>         System.out.print(combos.length + " combos - took ");
> > >>>>         System.out.print((System.currentTimeMillis() - start) + "
> > millis
> > >>>> - used ");
> > >>>>         Runtime rt = Runtime.getRuntime();
> > >>>>         System.out.println((rt.totalMemory() / 1048576) + "MB of
> > >>>> memory");
> > >>>>     }
> > >>>>
> > >>>> --
> > >>>> Jeremy Thomerson
> > >>>> http://www.wickettraining.com
> > >>>>
> > >>>>
> > >>>> On Thu, Dec 11, 2008 at 2:55 PM, Matthew Hanlon <mrhan...@gmail.com
> > >wrote:
> > >>>>
> > >>>>> I am looking for some feedback any may have on this:
> > >>>>> Let's say I've mounted a package "com.company.package" using
> > >>>>> PackageRequestTargetUrlCodingStrategy
> > >>>>> on "/foo." So I have several pages, /foo/Bar, /foo/Baz, etc. Now, I
> > >>>>> want my
> > >>>>> page mounts to be case-insensitive in the case that a user has caps
> > >>>>> lock on
> > >>>>> or types in all lower case or whatever. For
> > >>>>> PackageRequestTargetUrlCodingStrategy this works for the "/foo"
> part,
> > >>>>> but
> > >>>>> not the classname part, obviously.
> > >>>>>
> > >>>>> So I implemented a CaseInsensitiveClassResolver that delegates to a
> > >>>>> DefaultClassResolver. In the case that the DefaultClassResolver
> > cannot
> > >>>>> find
> > >>>>> the class, the CaseInsensitiveClassResolver tries to load the class
> > by
> > >>>>> trying different combinations of upper/lower case in the classname.
> > So,
> > >>>>> for
> > >>>>> "bar" it would try to resolve "com.company.package.Bar," "
> > >>>>> com.company.package.bAr," "com.company.package.baR," etc, obviously
> > >>>>> finding
> > >>>>> "com.company.package.Bar" and returning that class.
> > >>>>>
> > >>>>> This works pretty well. Now, obviously it's not the most efficient
> > >>>>> thing,
> > >>>>> possibly having to catch several
> > >>>>> ClassNotFoundException/NoClassDefFoundError exceptions
> > >>>>> before finding the class (assuming the name exists and is spelled
> > >>>>> correctly,
> > >>>>> just with wrong case). But it might be better than returning a 404
> on
> > a
> > >>>>> page
> > >>>>> simply due to improper case. I wouldn't expect it to happen often,
> as
> > >>>>> more
> > >>>>> often than not the user will probably use a link the get to the
> > pages,
> > >>>>> and
> > >>>>> so no typing at all. But in the rare case...
> > >>>>>
> > >>>>> So, any thoughts?
> > >>>>>
> > >>>>> Here's the code:
> > >>>>>
> > >>>>> public class CaseInsensitiveClassResolver implements IClassResolver
> {
> > >>>>>
> > >>>>> private static final Logger logger =
> > >>>>> LoggerFactory.getLogger(CaseInsensitiveClassResolver.class);
> > >>>>>  private DefaultClassResolver resolver = new
> DefaultClassResolver();
> > >>>>>  public Iterator<URL> getResources(String name) {
> > >>>>> return resolver.getResources(name);
> > >>>>> }
> > >>>>>
> > >>>>> public Class<?> resolveClass(String classname) {
> > >>>>> Class<?> clazz = null;
> > >>>>> try {
> > >>>>> clazz = resolver.resolveClass(classname);
> > >>>>> } catch (ClassNotFoundException e1) {
> > >>>>> clazz = resolveClassCaseInsensitive(classname);
> > >>>>> } catch (NoClassDefFoundError e2) {
> > >>>>> clazz = resolveClassCaseInsensitive(classname);
> > >>>>> }
> > >>>>> return clazz;
> > >>>>> }
> > >>>>>  public Class<?> resolveClassCaseInsensitive(String classname)
> throws
> > >>>>> ClassNotFoundException {
> > >>>>> if (logger.isDebugEnabled()) {
> > >>>>> logger.debug("Class not found for " + classname + ".  Trying to
> look
> > up
> > >>>>> case-insensitive.");
> > >>>>> }
> > >>>>> String packageName = classname.substring(0,
> > >>>>> classname.lastIndexOf('.'));
> > >>>>> String simpleName = classname.substring(classname.lastIndexOf('.')
> +
> > >>>>> 1);
> > >>>>>  String combos = capsCombinations(simpleName.toLowerCase(), 0);
> > >>>>> Class<?> cls = null;
> > >>>>> for (String combo : combos.split(",")) {
> > >>>>> try {
> > >>>>> cls = resolver.resolveClass(packageName + "." + combo);
> > >>>>> } catch (ClassNotFoundException e1) {
> > >>>>> if (logger.isDebugEnabled()) {
> > >>>>> logger.debug("Class not found for " + packageName + "." + combo +
> > ".");
> > >>>>> }
> > >>>>> } catch (NoClassDefFoundError e2) {
> > >>>>> if (logger.isDebugEnabled()) {
> > >>>>> logger.debug("Class not found for " + packageName + "." + combo +
> > ".");
> > >>>>> }
> > >>>>> }
> > >>>>> if (cls != null) {
> > >>>>> if (logger.isDebugEnabled()) {
> > >>>>> logger.debug("Class found for " + packageName + "." + combo + ".");
> > >>>>> }
> > >>>>> return cls;
> > >>>>> }
> > >>>>> }
> > >>>>> return null;
> > >>>>> }
> > >>>>>
> > >>>>> private String capsCombinations(String word, int startIndex) {
> > >>>>> StringBuilder sb = new StringBuilder(word);
> > >>>>> if (word.equals(word.toUpperCase())) {
> > >>>>> return sb.toString();
> > >>>>> } else {
> > >>>>> for (; startIndex < word.length();) {
> > >>>>> char[] chars = word.toCharArray();
> > >>>>> chars[startIndex] = Character.toUpperCase(chars[startIndex]);
> > >>>>> sb.append(",");
> > >>>>> sb.append(capsCombinations(new String(chars), ++startIndex));
> > >>>>> }
> > >>>>> return sb.toString();
> > >>>>> }
> > >>>>> }
> > >>>>> }
> > >>>>> --
> > >>>>> Matthew Rollins Hanlon
> > >>>>> http://squareoftwo.org
> > >>>>> _____________________
> > >>>>> Hanlon's Razor:
> > >>>>> "Never attribute to malice that which can be adequately explained
> by
> > >>>>> stupidity."
> > >>>>> http://wikipedia.org/wiki/Hanlon's_razor
> > >>>>>
> > >>>>
> > >>>>
> > >>>>
> > >>>>
> > >>>
> > >>>
> > >>> --
> > >>>  Matthew Rollins Hanlon
> > >>> http://squareoftwo.org
> > >>> _____________________
> > >>> Hanlon's Razor:
> > >>> "Never attribute to malice that which can be adequately explained by
> > >>> stupidity."
> > >>> http://wikipedia.org/wiki/Hanlon's_razor
> > >>>
> > >>
> > >>
> > >>
> > >> --
> > >> Jeremy Thomerson
> > >> http://www.wickettraining.com
> > >>
> > >
> > >
> > >
> > > --
> > > Matthew Rollins Hanlon
> > > http://squareoftwo.org
> > > _____________________
> > > Hanlon's Razor:
> > > "Never attribute to malice that which can be adequately explained by
> > > stupidity."
> > > http://wikipedia.org/wiki/Hanlon's_razor
> > >
> >
> >
> >
> > --
> >  Matthew Rollins Hanlon
> > http://squareoftwo.org
> > _____________________
> > Hanlon's Razor:
> > "Never attribute to malice that which can be adequately explained by
> > stupidity."
> > http://wikipedia.org/wiki/Hanlon's_razor
> >
>
>
>
> --
> Jeremy Thomerson
> http://www.wickettraining.com
>



-- 
Matthew Rollins Hanlon
http://squareoftwo.org
_____________________
Hanlon's Razor:
"Never attribute to malice that which can be adequately explained by
stupidity."
http://wikipedia.org/wiki/Hanlon's_razor

Reply via email to