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.api.ldap.model.schema.registries;
021
022
023import java.util.Collections;
024import java.util.HashMap;
025import java.util.Iterator;
026import java.util.List;
027import java.util.Map;
028
029import org.apache.commons.lang.ArrayUtils;
030import org.apache.directory.api.asn1.util.Oid;
031import org.apache.directory.api.i18n.I18n;
032import org.apache.directory.api.ldap.model.exception.LdapException;
033import org.apache.directory.api.ldap.model.schema.SchemaObject;
034import org.slf4j.Logger;
035import org.slf4j.LoggerFactory;
036
037
038/**
039 * Object identifier registry. It stores the OIDs for AT, OC, MR, LS, MRU, DSR, DCR and NF.
040 * An OID is unique, and associated with a SO.
041 * 
042 * @param <T> The type of SchemaObject
043 * 
044 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
045 */
046public class OidRegistry<T extends SchemaObject> implements Iterable<T>
047{
048    /** static class logger */
049    private static final Logger LOG = LoggerFactory.getLogger( OidRegistry.class );
050
051    /** Speedup for DEBUG mode */
052    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
053
054    /** Maps OID to a type of SchemaObject */
055    private Map<String, T> byOid = new HashMap<>();
056    
057    /** A flag indicating that the Registry is relaxed or not */
058    private boolean isRelaxed = Registries.STRICT;
059
060
061    /**
062     * Tells if the given OID is present on this registry
063     * 
064     * @param oid The OID to lookup
065     * @return true if the OID already exists
066     */
067    public boolean contains( String oid )
068    {
069        return byOid.containsKey( oid );
070    }
071
072
073    /**
074     * Gets the primary name associated with an OID.  The primary name is the
075     * first name specified for the OID.
076     * 
077     * @param oid the object identifier
078     * @return the primary name
079     * @throws LdapException if oid does not exist
080     */
081    public String getPrimaryName( String oid ) throws LdapException
082    {
083        SchemaObject schemaObject = byOid.get( oid );
084
085        if ( schemaObject != null )
086        {
087            return schemaObject.getName();
088        }
089        else
090        {
091            String msg = I18n.err( I18n.ERR_04286, oid );
092            LOG.error( msg );
093            throw new LdapException( msg );
094        }
095    }
096
097
098    /**
099     * Gets the SchemaObject associated with an OID. 
100     * 
101     * @param oid the object identifier
102     * @return the associated SchemaObject
103     * @throws LdapException if oid does not exist
104     */
105    public T getSchemaObject( String oid ) throws LdapException
106    {
107        T schemaObject = byOid.get( oid );
108
109        if ( schemaObject != null )
110        {
111            return schemaObject;
112        }
113        else
114        {
115            String msg = I18n.err( I18n.ERR_04287, oid );
116            LOG.error( msg );
117            throw new LdapException( msg );
118        }
119    }
120
121
122    /**
123     * Gets the names associated with an OID.  An OID is unique however it may 
124     * have many names used to refer to it.  A good example is the cn and
125     * commonName attribute names for OID 2.5.4.3.  Within a server one name 
126     * within the set must be chosen as the primary name.  This is used to
127     * name certain things within the server internally.  If there is more than
128     * one name then the first name is taken to be the primary.
129     * 
130     * @param oid the OID for which we return the set of common names
131     * @return a sorted set of names
132     * @throws org.apache.directory.api.ldap.model.exception.LdapException if oid does not exist
133     */
134    public List<String> getNameSet( String oid ) throws LdapException
135    {
136        SchemaObject schemaObject = byOid.get( oid );
137
138        if ( null == schemaObject )
139        {
140            String msg = I18n.err( I18n.ERR_04288, oid );
141            LOG.error( msg );
142            throw new LdapException( msg );
143        }
144
145        List<String> names = schemaObject.getNames();
146
147        if ( IS_DEBUG )
148        {
149            LOG.debug( "looked up names '{}' for OID '{}'", ArrayUtils.toString( names ), oid );
150        }
151
152        return names;
153    }
154
155
156    /**
157     * Lists all the OIDs within the registry.  This may be a really big list.
158     * 
159     * @return all the OIDs registered
160     */
161    public Iterator<String> iteratorOids()
162    {
163        return Collections.unmodifiableSet( byOid.keySet() ).iterator();
164    }
165
166
167    /**
168     * Lists all the SchemaObjects within the registry.  This may be a really big list.
169     * 
170     * @return all the SchemaObject registered
171     */
172    @Override
173    public Iterator<T> iterator()
174    {
175        return byOid.values().iterator();
176    }
177
178    
179    /**
180     * Tells if the Registry is permissive or if it must be checked
181     * against inconsistencies.
182     *
183     * @return True if SchemaObjects can be added even if they break the consistency
184     */
185    public boolean isRelaxed()
186    {
187        return isRelaxed;
188    }
189
190
191    /**
192     * Tells if the Registry is strict.
193     *
194     * @return True if SchemaObjects cannot be added if they break the consistency
195     */
196    public boolean isStrict()
197    {
198        return !isRelaxed;
199    }
200
201
202    /**
203     * Change the Registry to a relaxed mode, where invalid SchemaObjects
204     * can be registered.
205     */
206    public void setRelaxed()
207    {
208        isRelaxed = Registries.RELAXED;
209    }
210
211
212    /**
213     * Change the Registry to a strict mode, where invalid SchemaObjects
214     * cannot be registered.
215     */
216    public void setStrict()
217    {
218        isRelaxed = Registries.STRICT;
219    }
220
221    /**
222     * Adds an OID name pair to the registry.
223     * 
224     * @param schemaObject The SchemaObject the oid belongs to
225     * @throws LdapException If something went wrong
226     */
227    public void register( T schemaObject ) throws LdapException
228    {
229        if ( schemaObject == null )
230        {
231            String message = I18n.err( I18n.ERR_04289 );
232
233            LOG.debug( message );
234            throw new LdapException( message );
235        }
236
237        String oid = schemaObject.getOid();
238
239        if ( isStrict() )
240        {
241            if ( !Oid.isOid( oid ) )
242            {
243                String message = I18n.err( I18n.ERR_04290 );
244
245                LOG.debug( message );
246                throw new LdapException( message );
247            }
248        }
249        else
250        {
251            if ( ( oid == null ) || oid.isEmpty() )
252            {
253                throw new LdapException( I18n.err( I18n.ERR_00033_INVALID_OID, "" ) );
254            }
255        }
256
257        /*
258         * Update OID Map if it does not already exist
259         */
260        if ( byOid.containsKey( oid ) )
261        {
262            String message = I18n.err( I18n.ERR_04291, oid );
263            LOG.info( message );
264            return;
265        }
266        else
267        {
268            byOid.put( oid, schemaObject );
269
270            if ( IS_DEBUG )
271            {
272                LOG.debug( "registed SchemaObject '" + schemaObject + "' with OID: " + oid );
273            }
274        }
275    }
276
277
278    /**
279     * Store the given SchemaObject into the OidRegistry. Available only to 
280     * the current package. A weak form (no check is done) of the register 
281     * method, define for clone methods.
282     *
283     * @param schemaObject The SchemaObject to inject into the OidRegistry
284     */
285    /* No qualifier */void put( T schemaObject )
286    {
287        byOid.put( schemaObject.getOid(), schemaObject );
288    }
289
290
291    /**
292     * Removes an oid from this registry.
293     *
294     * @param oid the numeric identifier for the object
295     * @throws LdapException if the identifier is not numeric
296     */
297    public void unregister( String oid ) throws LdapException
298    {
299        // Removes the <OID, names> from the byOID map
300        SchemaObject removed = byOid.remove( oid );
301
302        if ( IS_DEBUG )
303        {
304            LOG.debug( "Unregisted SchemaObject '{}' with OID: {}", removed, oid );
305        }
306    }
307
308
309    /**
310     * Copy the OidRegistry, without the contained values
311     * 
312     * @return A new OidRegistry instance
313     */
314    public OidRegistry<T> copy()
315    {
316        OidRegistry<T> copy = new OidRegistry<>();
317
318        // Clone the map
319        copy.byOid = new HashMap<>();
320
321        return copy;
322    }
323
324
325    /**
326     * @return The number of stored OIDs
327     */
328    public int size()
329    {
330        return byOid.size();
331    }
332
333
334    /**
335     * Empty the byOid map
336     */
337    public void clear()
338    {
339        // remove all the OID
340        byOid.clear();
341    }
342
343
344    /**
345     * @see Object#toString()
346     */
347    @Override
348    public String toString()
349    {
350        StringBuilder sb = new StringBuilder();
351
352        if ( byOid != null )
353        {
354            boolean isFirst = true;
355
356            for ( Map.Entry<String, T> entry : byOid.entrySet() )
357            {
358                if ( isFirst )
359                {
360                    isFirst = false;
361                }
362                else
363                {
364                    sb.append( ", " );
365                }
366
367                sb.append( "<" );
368
369                SchemaObject schemaObject = entry.getValue();
370
371                if ( schemaObject != null )
372                {
373                    sb.append( schemaObject.getObjectType() );
374                    sb.append( ", " );
375                    sb.append( schemaObject.getOid() );
376                    sb.append( ", " );
377                    sb.append( schemaObject.getName() );
378                }
379
380                sb.append( ">" );
381            }
382        }
383
384        return sb.toString();
385    }
386}