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

Reply via email to