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.registries;
021    
022    
023    import java.util.HashMap;
024    import java.util.Iterator;
025    import java.util.Map;
026    
027    import org.apache.directory.shared.asn1.primitives.OID;
028    import org.apache.directory.shared.i18n.I18n;
029    import org.apache.directory.shared.ldap.exception.LdapAttributeInUseException;
030    import org.apache.directory.shared.ldap.exception.LdapException;
031    import org.apache.directory.shared.ldap.schema.LoadableSchemaObject;
032    import org.apache.directory.shared.ldap.schema.SchemaObject;
033    import org.apache.directory.shared.ldap.schema.SchemaObjectType;
034    import org.apache.directory.shared.ldap.util.StringTools;
035    import org.slf4j.Logger;
036    import org.slf4j.LoggerFactory;
037    
038    
039    /**
040     * Common schema object registry interface.
041     *
042     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
043     * @version $Rev$, $Date$
044     */
045    public abstract class DefaultSchemaObjectRegistry<T extends SchemaObject> implements SchemaObjectRegistry<T>,
046        Iterable<T>
047    {
048        /** static class logger */
049        private static final Logger LOG = LoggerFactory.getLogger( DefaultSchemaObjectRegistry.class );
050    
051        /** A speedup for debug */
052        private static final boolean DEBUG = LOG.isDebugEnabled();
053    
054        /** a map of SchemaObject looked up by name */
055        protected Map<String, T> byName;
056    
057        /** The SchemaObject type, used by the toString() method  */
058        protected SchemaObjectType schemaObjectType;
059    
060        /** the global OID Registry */
061        protected OidRegistry oidRegistry;
062    
063    
064        /**
065         * Creates a new DefaultSchemaObjectRegistry instance.
066         */
067        protected DefaultSchemaObjectRegistry( SchemaObjectType schemaObjectType, OidRegistry oidRegistry )
068        {
069            byName = new HashMap<String, T>();
070            this.schemaObjectType = schemaObjectType;
071            this.oidRegistry = oidRegistry;
072        }
073    
074    
075        /**
076         * {@inheritDoc}
077         */
078        public boolean contains( String oid )
079        {
080            if ( !byName.containsKey( oid ) )
081            {
082                return byName.containsKey( StringTools.toLowerCase( oid ) );
083            }
084    
085            return true;
086        }
087    
088    
089        /**
090         * {@inheritDoc}
091         */
092        public String getSchemaName( String oid ) throws LdapException
093        {
094            if ( !OID.isOID( oid ) )
095            {
096                String msg = I18n.err( I18n.ERR_04267 );
097                LOG.warn( msg );
098                throw new LdapException( msg );
099            }
100    
101            SchemaObject schemaObject = byName.get( oid );
102    
103            if ( schemaObject != null )
104            {
105                return schemaObject.getSchemaName();
106            }
107    
108            String msg = I18n.err( I18n.ERR_04268, oid );
109            LOG.warn( msg );
110            throw new LdapException( msg );
111        }
112    
113    
114        /**
115         * {@inheritDoc}
116         */
117        public void renameSchema( String originalSchemaName, String newSchemaName )
118        {
119            // Loop on all the SchemaObjects stored and remove those associated
120            // with the give schemaName
121            for ( T schemaObject : this )
122            {
123                if ( originalSchemaName.equalsIgnoreCase( schemaObject.getSchemaName() ) )
124                {
125                    schemaObject.setSchemaName( newSchemaName );
126    
127                    if ( DEBUG )
128                    {
129                        LOG.debug( "Renamed {} schemaName to {}", schemaObject, newSchemaName );
130                    }
131                }
132            }
133        }
134    
135    
136        /**
137         * {@inheritDoc}
138         */
139        public Iterator<T> iterator()
140        {
141            return ( Iterator<T> ) oidRegistry.iterator();
142        }
143    
144    
145        /**
146         * {@inheritDoc}
147         */
148        public Iterator<String> oidsIterator()
149        {
150            return byName.keySet().iterator();
151        }
152    
153    
154        /**
155         * {@inheritDoc}
156         */
157        public T lookup( String oid ) throws LdapException
158        {
159            if ( oid == null )
160            {
161                return null;
162            }
163    
164            T schemaObject = byName.get( oid );
165    
166            if ( schemaObject == null )
167            {
168                // let's try with trimming and lowercasing now
169                schemaObject = byName.get( StringTools.trim( StringTools.toLowerCase( oid ) ) );
170            }
171    
172            if ( schemaObject == null )
173            {
174                String msg = I18n.err( I18n.ERR_04269, schemaObjectType.name(), oid );
175                LOG.debug( msg );
176                throw new LdapException( msg );
177            }
178    
179            if ( DEBUG )
180            {
181                LOG.debug( "Found {} with oid: {}", schemaObject, oid );
182            }
183    
184            return schemaObject;
185        }
186    
187    
188        /**
189         * {@inheritDoc}
190         */
191        public void register( T schemaObject ) throws LdapException
192        {
193            String oid = schemaObject.getOid();
194    
195            if ( byName.containsKey( oid ) )
196            {
197                String msg = I18n.err( I18n.ERR_04270, schemaObjectType.name(), oid );
198                LOG.warn( msg );
199                throw new LdapAttributeInUseException( msg );
200            }
201    
202            byName.put( oid, schemaObject );
203    
204            /*
205             * add the aliases/names to the name map along with their toLowerCase
206             * versions of the name: this is used to make sure name lookups work
207             */
208            for ( String name : schemaObject.getNames() )
209            {
210                String lowerName = StringTools.trim( StringTools.toLowerCase( name ) );
211    
212                if ( byName.containsKey( lowerName ) )
213                {
214                    String msg = I18n.err( I18n.ERR_04271, schemaObjectType.name(), name );
215                    LOG.warn( msg );
216                    throw new LdapAttributeInUseException( msg );
217                }
218                else
219                {
220                    byName.put( lowerName, schemaObject );
221                }
222            }
223    
224            // And register the oid -> schemaObject relation
225            oidRegistry.register( schemaObject );
226    
227            if ( LOG.isDebugEnabled() )
228            {
229                LOG.debug( "registered " + schemaObject.getName() + " for OID {}", oid );
230            }
231        }
232    
233    
234        /**
235         * {@inheritDoc}
236         */
237        public T unregister( String numericOid ) throws LdapException
238        {
239            if ( !OID.isOID( numericOid ) )
240            {
241                String msg = I18n.err( I18n.ERR_04272, numericOid );
242                LOG.error( msg );
243                throw new LdapException( msg );
244            }
245    
246            T schemaObject = byName.remove( numericOid );
247    
248            for ( String name : schemaObject.getNames() )
249            {
250                byName.remove( name );
251            }
252    
253            // And remove the SchemaObject from the oidRegistry
254            oidRegistry.unregister( numericOid );
255    
256            if ( DEBUG )
257            {
258                LOG.debug( "Removed {} with oid {} from the registry", schemaObject, numericOid );
259            }
260    
261            return schemaObject;
262        }
263    
264    
265        /**
266         * {@inheritDoc}
267         */
268        public T unregister( T schemaObject ) throws LdapException
269        {
270            String oid = schemaObject.getOid();
271    
272            if ( !byName.containsKey( oid ) )
273            {
274                String msg = I18n.err( I18n.ERR_04273, schemaObjectType.name(), oid );
275                LOG.warn( msg );
276                throw new LdapException( msg );
277            }
278    
279            // Remove the oid
280            T removed = byName.remove( oid );
281    
282            /*
283             * Remove the aliases/names from the name map along with their toLowerCase
284             * versions of the name.
285             */
286            for ( String name : schemaObject.getNames() )
287            {
288                byName.remove( StringTools.trim( StringTools.toLowerCase( name ) ) );
289            }
290    
291            // And unregister the oid -> schemaObject relation
292            oidRegistry.unregister( oid );
293    
294            return removed;
295        }
296    
297    
298        /**
299         * {@inheritDoc}
300         */
301        public void unregisterSchemaElements( String schemaName ) throws LdapException
302        {
303            if ( schemaName == null )
304            {
305                return;
306            }
307    
308            // Loop on all the SchemaObjects stored and remove those associated
309            // with the give schemaName
310            for ( T schemaObject : this )
311            {
312                if ( schemaName.equalsIgnoreCase( schemaObject.getSchemaName() ) )
313                {
314                    String oid = schemaObject.getOid();
315                    SchemaObject removed = unregister( oid );
316    
317                    if ( DEBUG )
318                    {
319                        LOG.debug( "Removed {} with oid {} from the registry", removed, oid );
320                    }
321                }
322            }
323        }
324    
325    
326        /**
327         * {@inheritDoc}
328         */
329        public String getOidByName( String name ) throws LdapException
330        {
331            T schemaObject = byName.get( name );
332    
333            if ( schemaObject == null )
334            {
335                // last resort before giving up check with lower cased version
336                String lowerCased = name.toLowerCase();
337    
338                schemaObject = byName.get( lowerCased );
339    
340                // ok this name is not for a schema object in the registry
341                if ( schemaObject == null )
342                {
343                    throw new LdapException( I18n.err( I18n.ERR_04274, name ) );
344                }
345            }
346    
347            // we found the schema object by key on the first lookup attempt
348            return schemaObject.getOid();
349        }
350    
351    
352        /**
353         * {@inheritDoc}
354         */
355        public SchemaObjectRegistry<T> copy( SchemaObjectRegistry<T> original )
356        {
357            // Fill the byName and OidRegistry maps, the type has already be copied
358            for ( String key : ( ( DefaultSchemaObjectRegistry<T> ) original ).byName.keySet() )
359            {
360                // Clone each SchemaObject
361                T value = ( ( DefaultSchemaObjectRegistry<T> ) original ).byName.get( key );
362    
363                if ( value instanceof LoadableSchemaObject )
364                {
365                    // Update the data structure. 
366                    // Comparators, Normalizers and SyntaxCheckers aren't copied, 
367                    // they are immutable
368                    byName.put( key, value );
369    
370                    // Update the OidRegistry
371                    oidRegistry.put( value );
372                }
373                else
374                {
375                    T copiedValue = null;
376    
377                    // Copy the value if it's not already in the oidRegistry
378                    if ( oidRegistry.contains( value.getOid() ) )
379                    {
380                        try
381                        {
382                            copiedValue = ( T ) oidRegistry.getSchemaObject( value.getOid() );
383                        }
384                        catch ( LdapException ne )
385                        {
386                            // Can't happen
387                        }
388                    }
389                    else
390                    {
391                        copiedValue = ( T ) value.copy();
392                    }
393    
394                    // Update the data structure. 
395                    byName.put( key, copiedValue );
396    
397                    // Update the OidRegistry
398                    oidRegistry.put( copiedValue );
399                }
400            }
401    
402            return this;
403        }
404    
405    
406        /**
407         * {@inheritDoc}
408         */
409        public SchemaObject get( String oid )
410        {
411            try
412            {
413                return oidRegistry.getSchemaObject( oid );
414            }
415            catch ( LdapException ne )
416            {
417                return null;
418            }
419        }
420    
421    
422        /**
423         * {@inheritDoc}
424         */
425        public SchemaObjectType getType()
426        {
427            return schemaObjectType;
428        }
429    
430    
431        /**
432         * {@inheritDoc}
433         */
434        public int size()
435        {
436            return oidRegistry.size();
437        }
438    
439    
440        /**
441         * @see Object#toString()
442         */
443        public String toString()
444        {
445            StringBuilder sb = new StringBuilder();
446    
447            sb.append( schemaObjectType ).append( ": " );
448            boolean isFirst = true;
449    
450            for ( String name : byName.keySet() )
451            {
452                if ( isFirst )
453                {
454                    isFirst = false;
455                }
456                else
457                {
458                    sb.append( ", " );
459                }
460    
461                T schemaObject = byName.get( name );
462    
463                sb.append( '<' ).append( name ).append( ", " ).append( schemaObject.getOid() ).append( '>' );
464            }
465    
466            return sb.toString();
467        }
468    
469    
470        /**
471         * {@inheritDoc}
472         */
473        public void clear()
474        {
475            // Clear all the schemaObjects
476            for ( SchemaObject schemaObject : oidRegistry )
477            {
478                // Don't clear LoadableSchemaObject
479                if ( !( schemaObject instanceof LoadableSchemaObject ) )
480                {
481                    schemaObject.clear();
482                }
483            }
484    
485            // Remove the byName elements
486            byName.clear();
487    
488            // Clear the OidRegistry
489            oidRegistry.clear();
490        }
491    }