001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.schema; 028 029 030 031 import org.opends.server.admin.std.server.EqualityMatchingRuleCfg; 032 import org.opends.server.api.EqualityMatchingRule; 033 import org.opends.server.config.ConfigException; 034 import org.opends.server.protocols.asn1.ASN1OctetString; 035 import org.opends.server.types.ByteString; 036 import org.opends.server.types.DirectoryException; 037 import org.opends.server.types.InitializationException; 038 039 import static org.opends.server.schema.SchemaConstants.*; 040 import static org.opends.server.util.StaticUtils.*; 041 042 043 044 /** 045 * This class defines the caseIgnoreMatch matching rule defined in X.520 and 046 * referenced in RFC 2252. 047 */ 048 public class CaseIgnoreEqualityMatchingRule 049 extends EqualityMatchingRule 050 { 051 /** 052 * Creates a new instance of this caseIgnoreMatch matching rule. 053 */ 054 public CaseIgnoreEqualityMatchingRule() 055 { 056 super(); 057 } 058 059 060 061 /** 062 * {@inheritDoc} 063 */ 064 public void initializeMatchingRule(EqualityMatchingRuleCfg configuration) 065 throws ConfigException, InitializationException 066 { 067 // No initialization is required. 068 } 069 070 071 072 /** 073 * Retrieves the common name for this matching rule. 074 * 075 * @return The common name for this matching rule, or <CODE>null</CODE> if 076 * it does not have a name. 077 */ 078 public String getName() 079 { 080 return EMR_CASE_IGNORE_NAME; 081 } 082 083 084 085 /** 086 * Retrieves the OID for this matching rule. 087 * 088 * @return The OID for this matching rule. 089 */ 090 public String getOID() 091 { 092 return EMR_CASE_IGNORE_OID; 093 } 094 095 096 097 /** 098 * Retrieves the description for this matching rule. 099 * 100 * @return The description for this matching rule, or <CODE>null</CODE> if 101 * there is none. 102 */ 103 public String getDescription() 104 { 105 // There is no standard description for this matching rule. 106 return null; 107 } 108 109 110 111 /** 112 * Retrieves the OID of the syntax with which this matching rule is 113 * associated. 114 * 115 * @return The OID of the syntax with which this matching rule is associated. 116 */ 117 public String getSyntaxOID() 118 { 119 return SYNTAX_DIRECTORY_STRING_OID; 120 } 121 122 123 124 /** 125 * Retrieves the normalized form of the provided value, which is best suited 126 * for efficiently performing matching operations on that value. 127 * 128 * @param value The value to be normalized. 129 * 130 * @return The normalized version of the provided value. 131 * 132 * @throws DirectoryException If the provided value is invalid according to 133 * the associated attribute syntax. 134 */ 135 public ByteString normalizeValue(ByteString value) 136 throws DirectoryException 137 { 138 byte[] valueBytes = value.value(); 139 int valueLength = valueBytes.length; 140 141 // Find the first non-space character. 142 int startPos = 0; 143 while ((startPos < valueLength) && (valueBytes[startPos] == ' ')) 144 { 145 startPos++; 146 } 147 148 if (startPos == valueLength) 149 { 150 // This should only happen if the value is composed entirely of spaces. 151 // In that case, the normalized value is a single space. 152 return new ASN1OctetString(" "); 153 } 154 155 156 // Find the last non-space character; 157 int endPos = (valueLength-1); 158 while ((endPos > startPos) && (valueBytes[endPos] == ' ')) 159 { 160 endPos--; 161 } 162 163 164 // Assume that the value contains only ASCII characters and iterate through 165 // it a character at a time, converting uppercase letters to lowercase. If 166 // we find a non-ASCII character, then fall back on a more correct method. 167 StringBuilder buffer = new StringBuilder(endPos-startPos+1); 168 boolean lastWasSpace = false; 169 for (int i=startPos; i <= endPos; i++) 170 { 171 byte b = valueBytes[i]; 172 if ((b & 0x7F) != b) 173 { 174 return normalizeNonASCII(value); 175 } 176 177 switch (b) 178 { 179 case ' ': 180 if (! lastWasSpace) 181 { 182 buffer.append(' '); 183 lastWasSpace = true; 184 } 185 break; 186 case 'A': 187 buffer.append('a'); 188 lastWasSpace = false; 189 break; 190 case 'B': 191 buffer.append('b'); 192 lastWasSpace = false; 193 break; 194 case 'C': 195 buffer.append('c'); 196 lastWasSpace = false; 197 break; 198 case 'D': 199 buffer.append('d'); 200 lastWasSpace = false; 201 break; 202 case 'E': 203 buffer.append('e'); 204 lastWasSpace = false; 205 break; 206 case 'F': 207 buffer.append('f'); 208 lastWasSpace = false; 209 break; 210 case 'G': 211 buffer.append('g'); 212 lastWasSpace = false; 213 break; 214 case 'H': 215 buffer.append('h'); 216 lastWasSpace = false; 217 break; 218 case 'I': 219 buffer.append('i'); 220 lastWasSpace = false; 221 break; 222 case 'J': 223 buffer.append('j'); 224 lastWasSpace = false; 225 break; 226 case 'K': 227 buffer.append('k'); 228 lastWasSpace = false; 229 break; 230 case 'L': 231 buffer.append('l'); 232 lastWasSpace = false; 233 break; 234 case 'M': 235 buffer.append('m'); 236 lastWasSpace = false; 237 break; 238 case 'N': 239 buffer.append('n'); 240 lastWasSpace = false; 241 break; 242 case 'O': 243 buffer.append('o'); 244 lastWasSpace = false; 245 break; 246 case 'P': 247 buffer.append('p'); 248 lastWasSpace = false; 249 break; 250 case 'Q': 251 buffer.append('q'); 252 lastWasSpace = false; 253 break; 254 case 'R': 255 buffer.append('r'); 256 lastWasSpace = false; 257 break; 258 case 'S': 259 buffer.append('s'); 260 lastWasSpace = false; 261 break; 262 case 'T': 263 buffer.append('t'); 264 lastWasSpace = false; 265 break; 266 case 'U': 267 buffer.append('u'); 268 lastWasSpace = false; 269 break; 270 case 'V': 271 buffer.append('v'); 272 lastWasSpace = false; 273 break; 274 case 'W': 275 buffer.append('w'); 276 lastWasSpace = false; 277 break; 278 case 'X': 279 buffer.append('x'); 280 lastWasSpace = false; 281 break; 282 case 'Y': 283 buffer.append('y'); 284 lastWasSpace = false; 285 break; 286 case 'Z': 287 buffer.append('z'); 288 lastWasSpace = false; 289 break; 290 default: 291 buffer.append((char) b); 292 lastWasSpace = false; 293 break; 294 } 295 } 296 297 298 return new ASN1OctetString(buffer.toString()); 299 } 300 301 302 303 /** 304 * Normalizes a value that contains a non-ASCII string. 305 * 306 * @param value The non-ASCII value to normalize. 307 * 308 * @return The normalized form of the provided value. 309 */ 310 private ByteString normalizeNonASCII(ByteString value) 311 { 312 StringBuilder buffer = new StringBuilder(); 313 toLowerCase(value.value(), buffer, true); 314 315 int bufferLength = buffer.length(); 316 if (bufferLength == 0) 317 { 318 if (value.value().length > 0) 319 { 320 // This should only happen if the value is composed entirely of spaces. 321 // In that case, the normalized value is a single space. 322 return new ASN1OctetString(" "); 323 } 324 else 325 { 326 // The value is empty, so it is already normalized. 327 return new ASN1OctetString(); 328 } 329 } 330 331 332 // Replace any consecutive spaces with a single space. 333 for (int pos = bufferLength-1; pos > 0; pos--) 334 { 335 if (buffer.charAt(pos) == ' ') 336 { 337 if (buffer.charAt(pos-1) == ' ') 338 { 339 buffer.delete(pos, pos+1); 340 } 341 } 342 } 343 344 return new ASN1OctetString(buffer.toString()); 345 } 346 347 348 349 /** 350 * Indicates whether the two provided normalized values are equal to each 351 * other. 352 * 353 * @param value1 The normalized form of the first value to compare. 354 * @param value2 The normalized form of the second value to compare. 355 * 356 * @return <CODE>true</CODE> if the provided values are equal, or 357 * <CODE>false</CODE> if not. 358 */ 359 public boolean areEqual(ByteString value1, ByteString value2) 360 { 361 byte[] b1 = value1.value(); 362 byte[] b2 = value2.value(); 363 364 int length = b1.length; 365 if (b2.length != length) 366 { 367 return false; 368 } 369 370 for (int i=0; i < length; i++) 371 { 372 if (b1[i] != b2[i]) 373 { 374 return false; 375 } 376 } 377 378 return true; 379 } 380 } 381