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