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.AttributeSyntax;
033 import org.opends.server.api.EqualityMatchingRule;
034 import org.opends.server.api.MatchingRule;
035 import org.opends.server.config.ConfigException;
036 import org.opends.server.core.DirectoryServer;
037 import org.opends.server.protocols.asn1.ASN1OctetString;
038 import org.opends.server.types.AttributeType;
039 import org.opends.server.types.AttributeValue;
040 import org.opends.server.types.ByteString;
041 import org.opends.server.types.DirectoryException;
042 import org.opends.server.types.InitializationException;
043 import org.opends.server.types.NameForm;
044 import org.opends.server.types.ObjectClass;
045
046 import static org.opends.server.schema.SchemaConstants.*;
047 import static org.opends.server.util.StaticUtils.*;
048
049
050
051 /**
052 * This class implements the objectIdentifierFirstComponentMatch matching rule
053 * defined in X.520 and referenced in RFC 2252. This rule is intended for use
054 * with attributes whose values contain a set of parentheses enclosing a
055 * space-delimited set of names and/or name-value pairs (like attribute type or
056 * objectclass descriptions) in which the "first component" is the first item
057 * after the opening parenthesis.
058 */
059 public class ObjectIdentifierFirstComponentEqualityMatchingRule
060 extends EqualityMatchingRule
061 {
062 /**
063 * Creates a new instance of this integerFirstComponentMatch matching rule.
064 */
065 public ObjectIdentifierFirstComponentEqualityMatchingRule()
066 {
067 super();
068 }
069
070
071
072 /**
073 * {@inheritDoc}
074 */
075 public void initializeMatchingRule(EqualityMatchingRuleCfg configuration)
076 throws ConfigException, InitializationException
077 {
078 // No initialization is required.
079 }
080
081
082
083 /**
084 * Retrieves the common name for this matching rule.
085 *
086 * @return The common name for this matching rule, or <CODE>null</CODE> if
087 * it does not have a name.
088 */
089 public String getName()
090 {
091 return EMR_OID_FIRST_COMPONENT_NAME;
092 }
093
094
095
096 /**
097 * Retrieves the OID for this matching rule.
098 *
099 * @return The OID for this matching rule.
100 */
101 public String getOID()
102 {
103 return EMR_OID_FIRST_COMPONENT_OID;
104 }
105
106
107
108 /**
109 * Retrieves the description for this matching rule.
110 *
111 * @return The description for this matching rule, or <CODE>null</CODE> if
112 * there is none.
113 */
114 public String getDescription()
115 {
116 // There is no standard description for this matching rule.
117 return null;
118 }
119
120
121
122 /**
123 * Retrieves the OID of the syntax with which this matching rule is
124 * associated.
125 *
126 * @return The OID of the syntax with which this matching rule is associated.
127 */
128 public String getSyntaxOID()
129 {
130 return SYNTAX_OID_OID;
131 }
132
133
134
135 /**
136 * Retrieves the normalized form of the provided value, which is best suited
137 * for efficiently performing matching operations on that value.
138 *
139 * @param value The value to be normalized.
140 *
141 * @return The normalized version of the provided value.
142 *
143 * @throws DirectoryException If the provided value is invalid according to
144 * the associated attribute syntax.
145 */
146 public ByteString normalizeValue(ByteString value)
147 throws DirectoryException
148 {
149 StringBuilder buffer = new StringBuilder();
150 toLowerCase(value.value(), buffer, true);
151
152 int bufferLength = buffer.length();
153 if (bufferLength == 0)
154 {
155 if (value.value().length > 0)
156 {
157 // This should only happen if the value is composed entirely of spaces.
158 // In that case, the normalized value is a single space.
159 return new ASN1OctetString(" ");
160 }
161 else
162 {
163 // The value is empty, so it is already normalized.
164 return new ASN1OctetString();
165 }
166 }
167
168
169 // Replace any consecutive spaces with a single space.
170 for (int pos = bufferLength-1; pos > 0; pos--)
171 {
172 if (buffer.charAt(pos) == ' ')
173 {
174 if (buffer.charAt(pos-1) == ' ')
175 {
176 buffer.delete(pos, pos+1);
177 }
178 }
179 }
180
181 return new ASN1OctetString(buffer.toString());
182 }
183
184
185
186 /**
187 * Indicates whether the two provided normalized values are equal to each
188 * other.
189 *
190 * @param value1 The normalized form of the first value to compare.
191 * @param value2 The normalized form of the second value to compare.
192 *
193 * @return <CODE>true</CODE> if the provided values are equal, or
194 * <CODE>false</CODE> if not.
195 */
196 public boolean areEqual(ByteString value1, ByteString value2)
197 {
198 // For this purpose, the first value will be considered the attribute value,
199 // and the second the assertion value. The attribute value must start with
200 // an open parenthesis, followed by one or more spaces.
201 String value1String = value1.stringValue();
202 int value1Length = value1String.length();
203
204 if ((value1Length == 0) || (value1String.charAt(0) != '('))
205 {
206 // They cannot be equal if the attribute value is empty or doesn't start
207 // with an open parenthesis.
208 return false;
209 }
210
211 char c;
212 int pos = 1;
213 while ((pos < value1Length) && ((c = value1String.charAt(pos)) == ' '))
214 {
215 pos++;
216 }
217
218 if (pos >= value1Length)
219 {
220 // We hit the end of the value before finding a non-space character.
221 return false;
222 }
223
224
225 // The current position must be the start position for the value. Keep
226 // reading until we find the next space.
227 int startPos = pos++;
228 while ((pos < value1Length) && ((c = value1String.charAt(pos)) != ' '))
229 {
230 pos++;
231 }
232
233 if (pos >= value1Length)
234 {
235 // We hit the end of the value before finding the next space.
236 return false;
237 }
238
239
240 // Grab the substring between the start pos and the current pos. If it is
241 // equal to the string representation of the second value, then we have a
242 // match.
243 String oid = value1String.substring(startPos, pos);
244 String value2String = value2.stringValue();
245 if (oid.equals(value2String))
246 {
247 return true;
248 }
249
250
251 // Just because the two values did not match doesn't mean it's a total
252 // waste. See if the OID refers to a known element of any of the following
253 // types that can also be referred to by the name or OID of the second
254 // value:
255 // - Attribute types
256 // - Objectclasses
257 // - Attribute Syntax
258 // - Matching Rule
259 // - Name Form
260
261 AttributeType attrType1 = DirectoryServer.getAttributeType(oid);
262 if (attrType1 != null)
263 {
264 AttributeType attrType2 = DirectoryServer.getAttributeType(value2String);
265 if (attrType2 == null)
266 {
267 return false;
268 }
269 else
270 {
271 return attrType1.equals(attrType2);
272 }
273 }
274
275 ObjectClass oc1 = DirectoryServer.getObjectClass(oid);
276 if (oc1 != null)
277 {
278 ObjectClass oc2 = DirectoryServer.getObjectClass(value2String);
279 if (oc2 == null)
280 {
281 return false;
282 }
283 else
284 {
285 return oc1.equals(oc2);
286 }
287 }
288
289 AttributeSyntax syntax1 = DirectoryServer.getAttributeSyntax(oid, false);
290 if (syntax1 != null)
291 {
292 AttributeSyntax syntax2 = DirectoryServer.getAttributeSyntax(value2String,
293 false);
294 if (syntax2 == null)
295 {
296 return false;
297 }
298 else
299 {
300 return syntax1.equals(syntax2);
301 }
302 }
303
304 MatchingRule mr1 = DirectoryServer.getMatchingRule(oid);
305 if (mr1 != null)
306 {
307 MatchingRule mr2 = DirectoryServer.getMatchingRule(value2String);
308 if (mr2 == null)
309 {
310 return false;
311 }
312 else
313 {
314 return mr1.equals(mr2);
315 }
316 }
317
318 NameForm nf1 = DirectoryServer.getNameForm(oid);
319 if (nf1 != null)
320 {
321 NameForm nf2 = DirectoryServer.getNameForm(value2String);
322 if (nf2 == null)
323 {
324 return false;
325 }
326 else
327 {
328 return nf1.equals(nf2);
329 }
330 }
331
332
333 // At this point, we're out of things to try so it's not a match.
334 return false;
335 }
336
337
338
339 /**
340 * Generates a hash code for the provided attribute value. This version of
341 * the method will simply create a hash code from the normalized form of the
342 * attribute value. For matching rules explicitly designed to work in cases
343 * where byte-for-byte comparisons of normalized values is not sufficient for
344 * determining equality (e.g., if the associated attribute syntax is based on
345 * hashed or encrypted values), then this method must be overridden to provide
346 * an appropriate implementation for that case.
347 *
348 * @param attributeValue The attribute value for which to generate the hash
349 * code.
350 *
351 * @return The hash code generated for the provided attribute value.*/
352 public int generateHashCode(AttributeValue attributeValue)
353 {
354 // In this case, we'll always return the same value because the matching
355 // isn't based on the entire value.
356 return 1;
357 }
358 }
359