--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.iapi.types.WorkHorseForCollatorDatatypes\r
+ \r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.iapi.types;\r
+\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+import org.apache.derby.iapi.types.BooleanDataValue;\r
+\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import java.text.CollationElementIterator;\r
+import java.text.CollationKey;\r
+import java.text.RuleBasedCollator;\r
+\r
+/**\r
+ * WorkHorseForCollatorDatatypes class holds on to RuleBasedCollator,\r
+ * and the base SQLChar object for the collation sensitive SQLChar,\r
+ * SQLVarchar, SQLLongvarchar and SQLClob. This class uses RuleBasedCollator\r
+ * and SQLChar object in the collation sensitive methods to do the comparison. \r
+ * The reason for encapsulating this here is that the collation version of \r
+ * SQLChar, SQLVarchar, SQLLongvarchar and SQLClob do not all have to duplicate \r
+ * the code for collation sensitive methods. Instead, they can simply delegate\r
+ * the work to methods defined in this class. \r
+ */\r
+final class WorkHorseForCollatorDatatypes \r
+{\r
+ /** \r
+ * Use this object for collation on character datatype. This collator\r
+ * object is passed as a parameter to the constructor.\r
+ */\r
+ private RuleBasedCollator collatorForCharacterDatatypes;\r
+ /**\r
+ * collatorForCharacterDatatypes will be used on this SQLChar to determine\r
+ * collationElementsForString. The collationElementsForString is used by\r
+ * the like method to do Collator specific comparison.\r
+ * This SQLChar object is passed as a parameter to the constructor.\r
+ */\r
+ private SQLChar stringData;\r
+ /**\r
+ * Following is the array holding a series of collation elements for the\r
+ * string. It will be used in the like method. This gets initialized when\r
+ * the like method is first invoked. \r
+ */\r
+ private int[] collationElementsForString;\r
+ /** \r
+ * Number of valid collation elements in the array above. Note that this \r
+ * might be smaller than the actual size of the array above. Gets \r
+ * initialized when the like method is first invoked.\r
+ */\r
+ private int countOfCollationElements;\r
+\r
+ // For null strings, cKey = null.\r
+ private CollationKey cKey; \r
+\r
+ WorkHorseForCollatorDatatypes(\r
+ RuleBasedCollator collatorForCharacterDatatypes,\r
+ SQLChar stringData)\r
+ {\r
+ this.collatorForCharacterDatatypes = collatorForCharacterDatatypes;\r
+ this.stringData = stringData;\r
+ }\r
+ \r
+ /** @see SQLChar#stringCompare(SQLChar, SQLChar) */\r
+ int stringCompare(SQLChar str1, SQLChar str2)\r
+ throws StandardException\r
+ {\r
+ CollationKey ckey1 = str1.getCollationKey();\r
+ CollationKey ckey2 = str2.getCollationKey();\r
+ \r
+ /*\r
+ ** By convention, nulls sort High, and null == null\r
+ */\r
+ if (ckey1 == null || ckey2 == null)\r
+ {\r
+ if (ckey1 != null) // str2 == null\r
+ return -1;\r
+ if (ckey2 != null) // this == null\r
+ return 1;\r
+ return 0; // both == null\r
+ }\r
+\r
+ return ckey1.compareTo(ckey2);\r
+ }\r
+ \r
+ /**\r
+ * This method implements the like function for char (with no escape value).\r
+ * The difference in this method and the same method in SQLChar is that \r
+ * here we use special Collator object to do the comparison rather than\r
+ * using the Collator object associated with the default jvm locale.\r
+ *\r
+ * @param pattern The pattern to use\r
+ *\r
+ * @return A SQL boolean value telling whether the first operand is\r
+ * like the second operand\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ BooleanDataValue like(DataValueDescriptor pattern)\r
+ throws StandardException\r
+ {\r
+ Boolean likeResult;\r
+\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(\r
+ pattern instanceof CollationElementsInterface,\r
+ "Both the operands must be instances of CollationElementsInterface");\r
+ likeResult = Like.like(stringData.getCharArray(), \r
+ stringData.getLength(), \r
+ ((SQLChar)pattern).getCharArray(), \r
+ pattern.getLength(), \r
+ null, \r
+ 0,\r
+ collatorForCharacterDatatypes);\r
+\r
+ return SQLBoolean.truthValue(stringData ,\r
+ pattern,\r
+ likeResult);\r
+ }\r
+ \r
+ /**\r
+ * This method implements the like function for char with an escape value.\r
+ * \r
+ * @param pattern The pattern to use\r
+ * \r
+ * @return A SQL boolean value telling whether the first operand is\r
+ * like the second operand\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ BooleanDataValue like(DataValueDescriptor pattern, \r
+ DataValueDescriptor escape) throws StandardException\r
+ {\r
+ Boolean likeResult;\r
+\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(\r
+ pattern instanceof CollationElementsInterface &&\r
+ escape instanceof CollationElementsInterface,\r
+ "All three operands must be instances of CollationElementsInterface");\r
+ \r
+ // ANSI states a null escape yields 'unknown' results \r
+ //\r
+ // This method is only called when we have an escape clause, so this \r
+ // test is valid\r
+\r
+ if (escape.isNull())\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_ESCAPE_IS_NULL);\r
+ }\r
+\r
+ CollationElementsInterface escapeCharacter = (CollationElementsInterface) escape;\r
+\r
+ if (escapeCharacter.getCollationElementsForString() != null && \r
+ (escapeCharacter.getCountOfCollationElements() != 1))\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_INVALID_ESCAPE_CHARACTER,\r
+ new String(escapeCharacter.toString()));\r
+ }\r
+ likeResult = Like.like(stringData.getCharArray(), \r
+ stringData.getLength(), \r
+ ((SQLChar)pattern).getCharArray(), \r
+ pattern.getLength(), \r
+ ((SQLChar)escape).getCharArray(), \r
+ escape.getLength(),\r
+ collatorForCharacterDatatypes);\r
+\r
+ return SQLBoolean.truthValue(stringData,\r
+ pattern,\r
+ likeResult);\r
+ }\r
+\r
+ /**\r
+ * Get the RuleBasedCollator which is getting used for collation sensitive\r
+ * methods. \r
+ */\r
+ RuleBasedCollator getCollatorForCollation()\r
+ {\r
+ return(collatorForCharacterDatatypes);\r
+ }\r
+\r
+ /**\r
+ * This method returns the count of collation elements for SQLChar object.\r
+ * It method will return the correct value only if method \r
+ * getCollationElementsForString has been called previously on the SQLChar\r
+ * object. \r
+ *\r
+ * @return count of collation elements for this instance of CollatorSQLChar\r
+ */\r
+ int getCountOfCollationElements()\r
+ {\r
+ return countOfCollationElements;\r
+ }\r
+\r
+ /**\r
+ * This method translates the string into a series of collation elements.\r
+ * These elements will get used in the like method.\r
+ * \r
+ * @return an array of collation elements for the string\r
+ * @throws StandardException\r
+ */\r
+ int[] getCollationElementsForString()\r
+ throws StandardException\r
+ {\r
+ if (stringData.isNull())\r
+ {\r
+ return (int[]) null;\r
+ }\r
+\r
+\r
+\r
+ // Caching of collationElementsForString is not working properly, in \r
+ // order to cache it needs to get invalidated everytime the container\r
+ // type's value is changed - through any interface, eg: readExternal, \r
+ // setValue, ... To get proper behavior, disabling caching, and will\r
+ // file a performance enhancement to implement correct caching.\r
+ collationElementsForString = null;\r
+ countOfCollationElements = 0;\r
+\r
+\r
+ if (collationElementsForString != null)\r
+ {\r
+ return collationElementsForString;\r
+ }\r
+\r
+ // countOfCollationElements should always be 0 when \r
+ // collationElementsForString is null\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (countOfCollationElements != 0)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "countOfCollationElements expected to be 0, not " + \r
+ countOfCollationElements);\r
+ }\r
+ }\r
+ \r
+\r
+ collationElementsForString = new int[stringData.getLength()];\r
+\r
+ CollationElementIterator cei = \r
+ collatorForCharacterDatatypes.getCollationElementIterator(\r
+ stringData.getString());\r
+\r
+ int nextInt;\r
+ while ((nextInt = cei.next()) != CollationElementIterator.NULLORDER)\r
+ {\r
+ /* Believe it or not, a String might have more\r
+ * collation elements than characters.\r
+ * So, we handle that case by increasing the int array\r
+ * by 5 and copying array elements.\r
+ */\r
+ if (countOfCollationElements == collationElementsForString.length)\r
+ {\r
+ int[] expandedArray = new int[countOfCollationElements + 5];\r
+ System.arraycopy(collationElementsForString, 0, expandedArray, \r
+ 0, collationElementsForString.length);\r
+ collationElementsForString = expandedArray;\r
+ }\r
+ collationElementsForString[countOfCollationElements++] = nextInt;\r
+ }\r
+\r
+ return collationElementsForString;\r
+ }\r
+}\r