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