001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.shared.ldap.schema;
021    
022    
023    import java.util.List;
024    
025    import javax.naming.NamingException;
026    
027    import org.apache.directory.shared.i18n.I18n;
028    import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
029    import org.apache.directory.shared.ldap.message.ResultCodeEnum;
030    import org.apache.directory.shared.ldap.schema.comparators.ComparableComparator;
031    import org.apache.directory.shared.ldap.schema.normalizers.NoOpNormalizer;
032    import org.apache.directory.shared.ldap.schema.registries.Registries;
033    
034    
035    /**
036     * A matchingRule definition. MatchingRules associate a comparator and a
037     * normalizer, forming the basic tools necessary to assert actions against
038     * attribute values. MatchingRules are associated with a specific Syntax for the
039     * purpose of resolving a normalized form and for comparisons.
040     * <p>
041     * According to ldapbis [MODELS]:
042     * </p>
043     * 
044     * <pre>
045     *  4.1.3. Matching Rules
046     *  
047     *    Matching rules are used by servers to compare attribute values against
048     *    assertion values when performing Search and Compare operations.  They
049     *    are also used to identify the value to be added or deleted when
050     *    modifying entries, and are used when comparing a purported
051     *    distinguished name with the name of an entry.
052     *  
053     *    A matching rule specifies the syntax of the assertion value.
054     * 
055     *    Each matching rule is identified by an object identifier (OID) and,
056     *    optionally, one or more short names (descriptors).
057     * 
058     *    Matching rule definitions are written according to the ABNF:
059     * 
060     *      MatchingRuleDescription = LPAREN WSP
061     *          numericoid                ; object identifier
062     *          [ SP &quot;NAME&quot; SP qdescrs ]  ; short names (descriptors)
063     *          [ SP &quot;DESC&quot; SP qdstring ] ; description
064     *          [ SP &quot;OBSOLETE&quot; ]         ; not active
065     *          SP &quot;SYNTAX&quot; SP numericoid ; assertion syntax
066     *          extensions WSP RPAREN     ; extensions
067     * 
068     *    where:
069     *      [numericoid] is object identifier assigned to this matching rule;
070     *      NAME [qdescrs] are short names (descriptors) identifying this
071     *          matching rule;
072     *      DESC [qdstring] is a short descriptive string;
073     *      OBSOLETE indicates this matching rule is not active;
074     *      SYNTAX identifies the assertion syntax by object identifier; and
075     *      [extensions] describe extensions.
076     * </pre>
077     * 
078     * @see <a href="http://www.faqs.org/rfcs/rfc2252.html">RFC 2252 Section 4.5</a>
079     * @see <a
080     *      href="http://www.ietf.org/internet-drafts/draft-ietf-ldapbis-models-11.txt">ldapbis
081     *      [MODELS]</a>
082     * @see DescriptionUtils#getDescription(MatchingRule)
083     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
084     * @version $Rev: 919765 $
085     */
086    public class MatchingRule extends AbstractSchemaObject
087    {
088        /** The serialVersionUID */
089        private static final long serialVersionUID = 1L;
090    
091        /** The associated Comparator */
092        protected LdapComparator<? super Object> ldapComparator;
093    
094        /** The associated Normalizer */
095        protected Normalizer normalizer;
096    
097        /** The associated LdapSyntax */
098        protected LdapSyntax ldapSyntax;
099    
100        /** The associated LdapSyntax OID */
101        private String ldapSyntaxOid;
102    
103    
104        /**
105         * Creates a new instance of MatchingRule.
106         *
107         * @param oid The MatchingRule OID
108         * @param registries The Registries reference
109         */
110        public MatchingRule( String oid )
111        {
112            super( SchemaObjectType.MATCHING_RULE, oid );
113        }
114    
115    
116        /**
117         * Inject the MatchingRule into the registries, updating the references to
118         * other SchemaObject
119         *
120         * @param registries The Registries
121         * @exception If the addition failed
122         */
123        public void addToRegistries( List<Throwable> errors, Registries registries ) throws NamingException
124        {
125            if ( registries != null )
126            {
127                try
128                {
129                    // Gets the associated Comparator 
130                    ldapComparator = ( LdapComparator<? super Object> ) registries.getComparatorRegistry().lookup( oid );
131                }
132                catch ( NamingException ne )
133                {
134                    // Default to a catch all comparator
135                    ldapComparator = new ComparableComparator( oid );
136                }
137    
138                try
139                {
140                    // Gets the associated Normalizer
141                    normalizer = registries.getNormalizerRegistry().lookup( oid );
142                }
143                catch ( NamingException ne )
144                {
145                    // Default to the NoOp normalizer
146                    normalizer = new NoOpNormalizer( oid );
147                }
148    
149                try
150                {
151                    // Get the associated LdapSyntax
152                    ldapSyntax = registries.getLdapSyntaxRegistry().lookup( ldapSyntaxOid );
153                }
154                catch ( NamingException ne )
155                {
156                    // The Syntax is a mandatory element, it must exist.
157                    throw new LdapSchemaViolationException( I18n.err( I18n.ERR_04317 ),
158                        ResultCodeEnum.UNWILLING_TO_PERFORM );
159                }
160    
161                /**
162                 * Add the MR references (using and usedBy) : 
163                 * MR -> C
164                 * MR -> N
165                 * MR -> S
166                 */
167                if ( ldapComparator != null )
168                {
169                    registries.addReference( this, ldapComparator );
170                }
171    
172                if ( normalizer != null )
173                {
174                    registries.addReference( this, normalizer );
175                }
176    
177                if ( ldapSyntax != null )
178                {
179                    registries.addReference( this, ldapSyntax );
180                }
181    
182            }
183        }
184    
185    
186        /**
187         * Remove the MatchingRule from the registries, updating the references to
188         * other SchemaObject.
189         * 
190         * If one of the referenced SchemaObject does not exist (), 
191         * an exception is thrown.
192         *
193         * @param registries The Registries
194         * @exception If the MatchingRule is not valid 
195         */
196        public void removeFromRegistries( List<Throwable> errors, Registries registries ) throws NamingException
197        {
198            if ( registries != null )
199            {
200                /**
201                 * Remove the MR references (using and usedBy) : 
202                 * MR -> C
203                 * MR -> N
204                 * MR -> S
205                 */
206                if ( ldapComparator != null )
207                {
208                    registries.delReference( this, ldapComparator );
209                }
210    
211                if ( ldapSyntax != null )
212                {
213                    registries.delReference( this, ldapSyntax );
214                }
215    
216                if ( normalizer != null )
217                {
218                    registries.delReference( this, normalizer );
219                }
220            }
221        }
222    
223    
224        /**
225         * Gets the LdapSyntax used by this MatchingRule.
226         * 
227         * @return the LdapSyntax of this MatchingRule
228         */
229        public LdapSyntax getSyntax()
230        {
231            return ldapSyntax;
232        }
233    
234    
235        /**
236         * Gets the LdapSyntax OID used by this MatchingRule.
237         * 
238         * @return the LdapSyntax of this MatchingRule
239         * @throws NamingException if there is a failure resolving the object
240         */
241        public String getSyntaxOid()
242        {
243            return ldapSyntaxOid;
244        }
245    
246    
247        /**
248         * Sets the Syntax's OID
249         *
250         * @param oid The Syntax's OID
251         */
252        public void setSyntaxOid( String oid )
253        {
254            if ( !isReadOnly )
255            {
256                this.ldapSyntaxOid = oid;
257            }
258        }
259    
260    
261        /**
262         * Sets the Syntax
263         *
264         * @param oid The Syntax
265         */
266        public void setSyntax( LdapSyntax ldapSyntax )
267        {
268            if ( !isReadOnly )
269            {
270                this.ldapSyntax = ldapSyntax;
271                this.ldapSyntaxOid = ldapSyntax.getOid();
272            }
273        }
274    
275    
276        /**
277         * Update the associated Syntax, even if the SchemaObject is readOnly
278         *
279         * @param oid The Syntax
280         */
281        public void updateSyntax( LdapSyntax ldapSyntax )
282        {
283            this.ldapSyntax = ldapSyntax;
284            this.ldapSyntaxOid = ldapSyntax.getOid();
285        }
286    
287    
288        /**
289         * Gets the LdapComparator enabling the use of this MatchingRule for ORDERING
290         * and sorted indexing.
291         * 
292         * @return the ordering LdapComparator
293         * @throws NamingException if there is a failure resolving the object
294         */
295        public LdapComparator<? super Object> getLdapComparator()
296        {
297            return ldapComparator;
298        }
299    
300    
301        /**
302         * Sets the LdapComparator
303         *
304         * @param oid The LdapComparator
305         */
306        public void setLdapComparator( LdapComparator<?> ldapComparator )
307        {
308            if ( !isReadOnly )
309            {
310                this.ldapComparator = ( LdapComparator<? super Object> ) ldapComparator;
311            }
312        }
313    
314    
315        /**
316         * Update the associated Comparator, even if the SchemaObject is readOnly
317         *
318         * @param oid The LdapComparator
319         */
320        public void updateLdapComparator( LdapComparator<?> ldapComparator )
321        {
322            this.ldapComparator = ( LdapComparator<? super Object> ) ldapComparator;
323        }
324    
325    
326        /**
327         * Gets the Normalizer enabling the use of this MatchingRule for EQUALITY
328         * matching and indexing.
329         * 
330         * @return the associated normalizer
331         * @throws NamingException if there is a failure resolving the object
332         */
333        public Normalizer getNormalizer()
334        {
335            return normalizer;
336        }
337    
338    
339        /**
340         * Sets the Normalizer
341         *
342         * @param oid The Normalizer
343         */
344        public void setNormalizer( Normalizer normalizer )
345        {
346            if ( !isReadOnly )
347            {
348                this.normalizer = normalizer;
349            }
350        }
351    
352    
353        /**
354         * Update the associated Normalizer, even if the SchemaObject is readOnly
355         *
356         * @param oid The Normalizer
357         */
358        public void updateNormalizer( Normalizer normalizer )
359        {
360            this.normalizer = normalizer;
361        }
362    
363    
364        /**
365         * @see Object#toString()
366         */
367        public String toString()
368        {
369            return objectType + " " + DescriptionUtils.getDescription( this );
370        }
371    
372    
373        /**
374         * Copy an MatchingRule
375         */
376        public MatchingRule copy()
377        {
378            MatchingRule copy = new MatchingRule( oid );
379    
380            // Copy the SchemaObject common data
381            copy.copy( this );
382    
383            // All the references to other Registries object are set to null.
384            copy.ldapComparator = null;
385            copy.ldapSyntax = null;
386            copy.normalizer = null;
387    
388            // Copy the syntax OID
389            copy.ldapSyntaxOid = ldapSyntaxOid;
390    
391            return copy;
392        }
393    
394    
395        /**
396         * @see Object#equals()
397         */
398        public boolean equals( Object o )
399        {
400            if ( !super.equals( o ) )
401            {
402                return false;
403            }
404    
405            if ( !( o instanceof MatchingRule ) )
406            {
407                return false;
408            }
409    
410            MatchingRule that = ( MatchingRule ) o;
411    
412            // Check the Comparator
413            if ( ldapComparator != null )
414            {
415                if ( !ldapComparator.equals( that.ldapComparator ) )
416                {
417                    return false;
418                }
419            }
420            else
421            {
422                if ( that.ldapComparator != null )
423                {
424                    return false;
425                }
426            }
427    
428            // Check the Normalizer
429            if ( normalizer != null )
430            {
431                if ( !normalizer.equals( that.normalizer ) )
432                {
433                    return false;
434                }
435            }
436            else
437            {
438                if ( that.normalizer != null )
439                {
440                    return false;
441                }
442            }
443    
444            // Check the Syntax
445            if ( !compareOid( ldapSyntaxOid, that.ldapSyntaxOid ) )
446            {
447                return false;
448            }
449    
450            return ldapSyntax.equals( that.ldapSyntax );
451        }
452    
453    
454        /**
455         * {@inheritDoc}
456         */
457        public void clear()
458        {
459            // Clear the common elements
460            super.clear();
461    
462            // Clear the references
463            ldapComparator = null;
464            ldapSyntax = null;
465            normalizer = null;
466        }
467    }