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 >