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