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 java.util.List;
032
033 import org.opends.server.admin.std.server.SubstringMatchingRuleCfg;
034 import org.opends.server.api.SubstringMatchingRule;
035 import org.opends.server.config.ConfigException;
036 import org.opends.server.protocols.asn1.ASN1OctetString;
037 import org.opends.server.types.ByteString;
038 import org.opends.server.types.DirectoryException;
039 import org.opends.server.types.InitializationException;
040
041 import static org.opends.server.schema.SchemaConstants.*;
042 import static org.opends.server.util.StaticUtils.*;
043
044
045
046 /**
047 * This class implements the caseIgnoreListSubstringsMatch matching rule defined
048 * in X.520 and referenced in RFC 2252.
049 */
050 public class CaseIgnoreListSubstringMatchingRule
051 extends SubstringMatchingRule
052 {
053 /**
054 * Creates a new instance of this caseIgnoreSubstringsMatch matching rule.
055 */
056 public CaseIgnoreListSubstringMatchingRule()
057 {
058 super();
059 }
060
061
062
063 /**
064 * {@inheritDoc}
065 */
066 public void initializeMatchingRule(SubstringMatchingRuleCfg configuration)
067 throws ConfigException, InitializationException
068 {
069 // No initialization is required.
070 }
071
072
073
074 /**
075 * Retrieves the common name for this matching rule.
076 *
077 * @return The common name for this matching rule, or <CODE>null</CODE> if
078 * it does not have a name.
079 */
080 public String getName()
081 {
082 return SMR_CASE_IGNORE_LIST_NAME;
083 }
084
085
086
087 /**
088 * Retrieves the OID for this matching rule.
089 *
090 * @return The OID for this matching rule.
091 */
092 public String getOID()
093 {
094 return SMR_CASE_IGNORE_LIST_OID;
095 }
096
097
098
099 /**
100 * Retrieves the description for this matching rule.
101 *
102 * @return The description for this matching rule, or <CODE>null</CODE> if
103 * there is none.
104 */
105 public String getDescription()
106 {
107 // There is no standard description for this matching rule.
108 return null;
109 }
110
111
112
113 /**
114 * Retrieves the OID of the syntax with which this matching rule is
115 * associated.
116 *
117 * @return The OID of the syntax with which this matching rule is associated.
118 */
119 public String getSyntaxOID()
120 {
121 return SYNTAX_SUBSTRING_ASSERTION_OID;
122 }
123
124
125
126 /**
127 * Retrieves the normalized form of the provided value, which is best suited
128 * for efficiently performing matching operations on that value.
129 *
130 * @param value The value to be normalized.
131 *
132 * @return The normalized version of the provided value.
133 *
134 * @throws DirectoryException If the provided value is invalid according to
135 * the associated attribute syntax.
136 */
137 public ByteString normalizeValue(ByteString value)
138 throws DirectoryException
139 {
140 StringBuilder buffer = new StringBuilder();
141 toLowerCase(value.value(), buffer, true);
142
143 int bufferLength = buffer.length();
144 if (bufferLength == 0)
145 {
146 if (value.value().length > 0)
147 {
148 // This should only happen if the value is composed entirely of spaces.
149 // In that case, the normalized value is a single space.
150 return new ASN1OctetString(" ");
151 }
152 else
153 {
154 // The value is empty, so it is already normalized.
155 return new ASN1OctetString();
156 }
157 }
158
159
160 // Replace any consecutive spaces with a single space. Any spaces around a
161 // dollar sign will also be removed.
162 for (int pos = bufferLength-1; pos > 0; pos--)
163 {
164 if (buffer.charAt(pos) == ' ')
165 {
166 char c = buffer.charAt(pos-1);
167 if (c == ' ')
168 {
169 buffer.delete(pos, pos+1);
170 }
171 else if (c == '$')
172 {
173 if ((pos <= 1) || (buffer.charAt(pos-2) != '\\'))
174 {
175 buffer.delete(pos, pos+1);
176 }
177 }
178 else if (buffer.charAt(pos+1) == '$')
179 {
180 buffer.delete(pos, pos+1);
181 }
182 }
183 }
184
185 return new ASN1OctetString(buffer.toString());
186 }
187
188
189
190 /**
191 * Normalizes the provided value fragment into a form that can be used to
192 * efficiently compare values.
193 *
194 * @param substring The value fragment to be normalized.
195 *
196 * @return The normalized form of the value fragment.
197 *
198 * @throws DirectoryException If the provided value fragment is not
199 * acceptable according to the associated syntax.
200 */
201 public ByteString normalizeSubstring(ByteString substring)
202 throws DirectoryException
203 {
204 // In this case, the process for normalizing a substring is the same as
205 // normalizing a full value with the exception that it may include an
206 // opening or trailing space.
207 StringBuilder buffer = new StringBuilder();
208 toLowerCase(substring.value(), buffer, false);
209
210 int bufferLength = buffer.length();
211 if (bufferLength == 0)
212 {
213 if (substring.value().length > 0)
214 {
215 // This should only happen if the value is composed entirely of spaces.
216 // In that case, the normalized value is a single space.
217 return new ASN1OctetString(" ");
218 }
219 else
220 {
221 // The value is empty, so it is already normalized.
222 return substring;
223 }
224 }
225
226
227 // Replace any consecutive spaces with a single space.
228 for (int pos = bufferLength-1; pos > 0; pos--)
229 {
230 if (buffer.charAt(pos) == ' ')
231 {
232 if (buffer.charAt(pos-1) == ' ')
233 {
234 buffer.delete(pos, pos+1);
235 }
236 }
237 }
238
239 return new ASN1OctetString(buffer.toString());
240 }
241
242
243
244 /**
245 * Determines whether the provided value matches the given substring filter
246 * components. Note that any of the substring filter components may be
247 * <CODE>null</CODE> but at least one of them must be non-<CODE>null</CODE>.
248 *
249 * @param value The normalized value against which to compare the
250 * substring components.
251 * @param subInitial The normalized substring value fragment that should
252 * appear at the beginning of the target value.
253 * @param subAnyElements The normalized substring value fragments that
254 * should appear in the middle of the target value.
255 * @param subFinal The normalized substring value fragment that should
256 * appear at the end of the target value.
257 *
258 * @return <CODE>true</CODE> if the provided value does match the given
259 * substring components, or <CODE>false</CODE> if not.
260 */
261 public boolean valueMatchesSubstring(ByteString value, ByteString subInitial,
262 List<ByteString> subAnyElements,
263 ByteString subFinal)
264 {
265 byte[] valueBytes = value.value();
266 int valueLength = valueBytes.length;
267
268 int pos = 0;
269 if (subInitial != null)
270 {
271 byte[] initialBytes = subInitial.value();
272 int initialLength = initialBytes.length;
273 if (initialLength > valueLength)
274 {
275 return false;
276 }
277
278 for (; pos < initialLength; pos++)
279 {
280 if (initialBytes[pos] != valueBytes[pos])
281 {
282 return false;
283 }
284 }
285 }
286
287
288 if ((subAnyElements != null) && (! subAnyElements.isEmpty()))
289 {
290 for (ByteString element : subAnyElements)
291 {
292 byte[] anyBytes = element.value();
293 int anyLength = anyBytes.length;
294
295 int end = valueLength - anyLength;
296 boolean match = false;
297 for (; pos <= end; pos++)
298 {
299 if (anyBytes[0] == valueBytes[pos])
300 {
301 boolean subMatch = true;
302 for (int i=1; i < anyLength; i++)
303 {
304 if (anyBytes[i] != valueBytes[pos+i])
305 {
306 subMatch = false;
307 break;
308 }
309 }
310
311 if (subMatch)
312 {
313 match = subMatch;
314 break;
315 }
316 }
317 }
318
319 if (match)
320 {
321 pos += anyLength;
322 }
323 else
324 {
325 return false;
326 }
327 }
328 }
329
330
331 if (subFinal != null)
332 {
333 byte[] finalBytes = subFinal.value();
334 int finalLength = finalBytes.length;
335
336 if ((valueLength - finalLength) < pos)
337 {
338 return false;
339 }
340
341 pos = valueLength - finalLength;
342 for (int i=0; i < finalLength; i++,pos++)
343 {
344 if (finalBytes[i] != valueBytes[pos])
345 {
346 return false;
347 }
348 }
349 }
350
351
352 return true;
353 }
354 }
355