Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[eclipse-dev] New CamelCase implementation in CharOperation

I've never submitted a patch before. Please let me know if there is a better way (e.g. file a bug report). I thought the list would be a better place to get some feedback.

I've re-written the camelcase match procedure on CharOperation. This is the one used by CTRL-SPC completion (though, I think, not for "Open Type". I don't know why there are two implementations -- I'll look into it later).

The new method is much more straightforward (I think), and it works correctly for a more useful definition of camel case, where lowercase may be used between the uppercases, and not only in the end. E.g., before, "TZon" would match "TimeZone", but "TiZo" would not!

I've also created a unit test for the method (for which I could not find a unit test). The MATCHES and MIS_MATCHES in the test (also pasted below) provide concrete examples of usage for the new method.

Find a patch attached.

Feedback is welcomed, of course, and help on how to take it from here.

   String[][] MATCHES = {
            {"TZ","TimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
            {"TiZ","TimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
            {"TiZon","TimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
            {"TZon","TimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
            {"TZone","TimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
            {"TimeZone","TimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
            {"TimeZ","TimeZ"},  //$NON-NLS-1$//$NON-NLS-2$
            {"TZ","TimeZ"},  //$NON-NLS-1$//$NON-NLS-2$
            {"T","TimeZ"},  //$NON-NLS-1$//$NON-NLS-2$
            {"T","TimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
            {"TZ","TZ"},  //$NON-NLS-1$//$NON-NLS-2$
            {"aT","aTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
            {"aTi","aTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
            {"aTiZ","aTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
            {"aTZ","aTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
            {"aT","artTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
            {"aTi","artTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
            {"aTiZ","artTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
            {"aTZ","artTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
    };

    String[][] MIS_MATCHES = {
            {"TZ","Timezone"},  //$NON-NLS-1$//$NON-NLS-2$
            {"aTZ","TimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
            {"aTZ","TZ"},  //$NON-NLS-1$//$NON-NLS-2$
            {"arT","aTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
            {"arTi","aTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
            {"arTiZ","aTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
            {"arTZ","aTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
            {"aT","atimeZone"},  //$NON-NLS-1$//$NON-NLS-2$

Z
### Eclipse Workspace Patch 1.0
#P org.eclipse.jdt.core.tests.compiler
Index: src/org/eclipse/jdt/core/tests/compiler/regression/CharOperationTest.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CharOperationTest.java,v
retrieving revision 1.1
diff -u -r1.1 CharOperationTest.java
--- src/org/eclipse/jdt/core/tests/compiler/regression/CharOperationTest.java	25 Nov 2005 17:26:24 -0000	1.1
+++ src/org/eclipse/jdt/core/tests/compiler/regression/CharOperationTest.java	14 Feb 2006 00:12:16 -0000
@@ -11,6 +11,7 @@
 package org.eclipse.jdt.core.tests.compiler.regression;
 
 import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
 
 import junit.framework.Test;
 
@@ -77,4 +78,51 @@
 public static Class testClass() {
 	return CharOperationTest.class;
 }
+
+public void testCamel() {
+    String[][] MATCHES = {
+            {"TZ","TimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"TiZ","TimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"TiZon","TimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"TZon","TimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"TZone","TimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"TimeZone","TimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"TimeZ","TimeZ"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"TZ","TimeZ"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"T","TimeZ"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"T","TimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"TZ","TZ"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"aT","aTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"aTi","aTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"aTiZ","aTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"aTZ","aTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"aT","artTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"aTi","artTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"aTiZ","artTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"aTZ","artTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
+    };
+    
+    for (int i = 0; i<MATCHES.length ; i++) {
+        String[] match = MATCHES[i];
+        assertTrue (match[0] + ":" + match[1], CharOperation.camelCaseMatch(match[0].toCharArray(), match[1].toCharArray())); //$NON-NLS-1$
+    }
+    
+    String[][] MIS_MATCHES = {
+            {"TZ","Timezone"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"aTZ","TimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"aTZ","TZ"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"arT","aTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"arTi","aTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"arTiZ","aTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"arTZ","aTimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
+            {"aT","atimeZone"},  //$NON-NLS-1$//$NON-NLS-2$
+    };
+    
+    for (int i = 0; i<MIS_MATCHES.length ; i++) {
+        String[] match = MIS_MATCHES[i];
+        assertFalse (match[0] + ":" + match[1], CharOperation.camelCaseMatch(match[0].toCharArray(), match[1].toCharArray())); //$NON-NLS-1$
+    }
+    
+}
+
 }
#P org.eclipse.jdt.core
Index: compiler/org/eclipse/jdt/core/compiler/CharOperation.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/CharOperation.java,v
retrieving revision 1.64
diff -u -r1.64 CharOperation.java
--- compiler/org/eclipse/jdt/core/compiler/CharOperation.java	10 Feb 2006 16:14:04 -0000	1.64
+++ compiler/org/eclipse/jdt/core/compiler/CharOperation.java	14 Feb 2006 00:12:19 -0000
@@ -284,64 +284,79 @@
 		return false;
 	}
 	char patternChar, nameChar;
-	int iPattern = patternStart+1;
-	int iName = nameStart+1;
-	nextPatternChar: while (iPattern < patternEnd) {
-		// check patternChar, keep camelCasing only if uppercase
-		if ((patternChar = pattern[iPattern]) < ScannerHelper.MAX_OBVIOUS) {
-			switch (ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[patternChar]) {
-				case ScannerHelper.C_UPPER_LETTER :
-					// still uppercase
-					break;
-				default:
-					// end of camelCase part of pattern
-					break nextPatternChar;
-			}
-		} else if (Character.isJavaIdentifierPart(patternChar) 
-						&& !Character.isUpperCase(patternChar)) {
-			// end of camelCase part of pattern
-			break nextPatternChar;
-		}
-		nextNameChar: while (iName < nameEnd) {
-			if ((nameChar = name[iName]) != patternChar) {
-				if (nameChar < ScannerHelper.MAX_OBVIOUS) {
-					switch (ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[nameChar]) {
-						case ScannerHelper.C_LOWER_LETTER :
-						case ScannerHelper.C_IDENT_PART :
-						case ScannerHelper.C_DIGIT :
-							// lowercase/digit char is ignored
-							iName++;
-							continue nextNameChar;
-					}
-				} else if (Character.isJavaIdentifierPart(nameChar) 
-								&& !Character.isUpperCase(nameChar)) {
-					// lowercase name char is ignored
-					iName++;
-					continue nextNameChar;
-				}
-				// mismatch, either uppercase in name or non case char ('/' etc)--> reject
-				return false;
-			} else {
-				// pattern char == name char (uppercase)
-				iName++;
-				iPattern++;
-				continue nextPatternChar;
-			}	
-		}
-		if (iPattern == patternEnd) return true;
-		if (iName == nameEnd) return false;
-		continue nextPatternChar;
-	}
+	
+	int iPattern = patternStart;
+	int iName = nameStart;
+	
+	while (true) {
 		
-	// check trailing part in case sensitive way
-	while (iPattern < patternEnd && iName < nameEnd) {
-		if (pattern[iPattern] != name[iName]) {
-			return false;
-		}
 		iPattern++;
 		iName++;
+		
+		if (iPattern == patternEnd) {
+			// We have exhausted pattern, so it's a match
+			return true;
+		}
+		
+		if (iName == nameEnd){
+			// We have exhausted name (and not pattern), so it's not a match 
+			return false;
+		}
+		
+		patternChar = pattern[iPattern];
+		
+		// For as long as we're exactly matching, bring it on
+		if (patternChar == name[iName])
+			continue;
+		        
+		if ( 
+              (patternChar < ScannerHelper.MAX_OBVIOUS 
+			    && 
+              ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[patternChar] != ScannerHelper.C_UPPER_LETTER)
+			||
+              (Character.isJavaIdentifierPart(patternChar)
+                &&
+              !Character.isUpperCase(patternChar))
+		) {
+			// patternChar is lowercase, and it does not match name, so it's not a match
+			return false;
+		}
+		
+		// patternChar is uppercase, so let's find the next uppercase in name
+		while (true) {
+			if (iName == nameEnd){
+            //	We have exhausted name (and not pattern), so it's not a match
+				return false;
+			}
+			
+			nameChar = name[iName];
+		         
+			if ( 
+		              (nameChar < ScannerHelper.MAX_OBVIOUS 
+					    && 
+		              ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[nameChar] != ScannerHelper.C_UPPER_LETTER)
+					||
+		              (Character.isJavaIdentifierPart(nameChar)
+		                &&
+		              !Character.isUpperCase(nameChar))
+				) {
+				// nameChar is lowercase    
+				iName++;
+			} else {
+				// nameChar is uppercase... 
+				if (patternChar != nameChar)
+					//.. and it does not match patternChar, so it's not a match
+					return false;
+				
+				//.. and it matched patternChar. Back to the big loop
+				break;
+			}
+		}
+		
+		// At this point, either name has been exhausted, or it is at an uppercase letter.
+		// Since pattern is also at an 
+			
 	}
-	return iPattern == patternEnd;
 }	
 
 /**

Back to the top