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