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 defines the caseIgnoreSubstringsMatch matching rule defined in
048 * X.520 and referenced in RFC 2252.
049 */
050 public class CaseIgnoreSubstringMatchingRule
051 extends SubstringMatchingRule
052 {
053 /**
054 * Creates a new instance of this caseIgnoreSubstringsMatch matching rule.
055 */
056 public CaseIgnoreSubstringMatchingRule()
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_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_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.
161 for (int pos = bufferLength-1; pos > 0; pos--)
162 {
163 if (buffer.charAt(pos) == ' ')
164 {
165 if (buffer.charAt(pos-1) == ' ')
166 {
167 buffer.delete(pos, pos+1);
168 }
169 }
170 }
171
172 return new ASN1OctetString(buffer.toString());
173 }
174
175
176
177 /**
178 * Normalizes the provided value fragment into a form that can be used to
179 * efficiently compare values.
180 *
181 * @param substring The value fragment to be normalized.
182 *
183 * @return The normalized form of the value fragment.
184 *
185 * @throws DirectoryException If the provided value fragment is not
186 * acceptable according to the associated syntax.
187 */
188 public ByteString normalizeSubstring(ByteString substring)
189 throws DirectoryException
190 {
191 // In this case, the process for normalizing a substring is the same as
192 // normalizing a full value with the exception that it may include an
193 // opening or trailing space.
194 StringBuilder buffer = new StringBuilder();
195 toLowerCase(substring.value(), buffer, false);
196
197 int bufferLength = buffer.length();
198 if (bufferLength == 0)
199 {
200 if (substring.value().length > 0)
201 {
202 // This should only happen if the value is composed entirely of spaces.
203 // In that case, the normalized value is a single space.
204 return new ASN1OctetString(" ");
205 }
206 else
207 {
208 // The value is empty, so it is already normalized.
209 return substring;
210 }
211 }
212
213
214 // Replace any consecutive spaces with a single space.
215 for (int pos = bufferLength-1; pos > 0; pos--)
216 {
217 if (buffer.charAt(pos) == ' ')
218 {
219 if (buffer.charAt(pos-1) == ' ')
220 {
221 buffer.delete(pos, pos+1);
222 }
223 }
224 }
225
226 return new ASN1OctetString(buffer.toString());
227 }
228
229
230
231 /**
232 * Determines whether the provided value matches the given substring filter
233 * components. Note that any of the substring filter components may be
234 * <CODE>null</CODE> but at least one of them must be non-<CODE>null</CODE>.
235 *
236 * @param value The normalized value against which to compare the
237 * substring components.
238 * @param subInitial The normalized substring value fragment that should
239 * appear at the beginning of the target value.
240 * @param subAnyElements The normalized substring value fragments that
241 * should appear in the middle of the target value.
242 * @param subFinal The normalized substring value fragment that should
243 * appear at the end of the target value.
244 *
245 * @return <CODE>true</CODE> if the provided value does match the given
246 * substring components, or <CODE>false</CODE> if not.
247 */
248 public boolean valueMatchesSubstring(ByteString value, ByteString subInitial,
249 List<ByteString> subAnyElements,
250 ByteString subFinal)
251 {
252 byte[] valueBytes = value.value();
253 int valueLength = valueBytes.length;
254
255 int pos = 0;
256 if (subInitial != null)
257 {
258 byte[] initialBytes = subInitial.value();
259 int initialLength = initialBytes.length;
260 if (initialLength > valueLength)
261 {
262 return false;
263 }
264
265 for (; pos < initialLength; pos++)
266 {
267 if (initialBytes[pos] != valueBytes[pos])
268 {
269 return false;
270 }
271 }
272 }
273
274
275 if ((subAnyElements != null) && (! subAnyElements.isEmpty()))
276 {
277 for (ByteString element : subAnyElements)
278 {
279 byte[] anyBytes = element.value();
280 int anyLength = anyBytes.length;
281
282 int end = valueLength - anyLength;
283 boolean match = false;
284 for (; pos <= end; pos++)
285 {
286 if (anyBytes[0] == valueBytes[pos])
287 {
288 boolean subMatch = true;
289 for (int i=1; i < anyLength; i++)
290 {
291 if (anyBytes[i] != valueBytes[pos+i])
292 {
293 subMatch = false;
294 break;
295 }
296 }
297
298 if (subMatch)
299 {
300 match = subMatch;
301 break;
302 }
303 }
304 }
305
306 if (match)
307 {
308 pos += anyLength;
309 }
310 else
311 {
312 return false;
313 }
314 }
315 }
316
317
318 if (subFinal != null)
319 {
320 byte[] finalBytes = subFinal.value();
321 int finalLength = finalBytes.length;
322
323 if ((valueLength - finalLength) < pos)
324 {
325 return false;
326 }
327
328 pos = valueLength - finalLength;
329 for (int i=0; i < finalLength; i++,pos++)
330 {
331 if (finalBytes[i] != valueBytes[pos])
332 {
333 return false;
334 }
335 }
336 }
337
338
339 return true;
340 }
341 }
342