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