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