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.HashSet; 026import java.util.Iterator; 027import java.util.Map; 028import java.util.Set; 029 030import org.apache.directory.api.i18n.I18n; 031import org.apache.directory.api.ldap.model.exception.LdapException; 032import org.apache.directory.api.ldap.model.exception.LdapNoSuchAttributeException; 033import org.apache.directory.api.ldap.model.exception.LdapSchemaException; 034import org.apache.directory.api.ldap.model.exception.LdapSchemaExceptionCodes; 035import org.apache.directory.api.ldap.model.schema.AttributeType; 036import org.apache.directory.api.ldap.model.schema.MatchingRule; 037import org.apache.directory.api.ldap.model.schema.SchemaObjectType; 038import org.apache.directory.api.ldap.model.schema.normalizers.NoOpNormalizer; 039import org.apache.directory.api.ldap.model.schema.normalizers.OidNormalizer; 040import org.apache.directory.api.util.Strings; 041import org.slf4j.Logger; 042import org.slf4j.LoggerFactory; 043 044 045/** 046 * An AttributeType registry service default implementation. 047 * 048 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 049 */ 050public class DefaultAttributeTypeRegistry extends DefaultSchemaObjectRegistry<AttributeType> implements 051 AttributeTypeRegistry 052{ 053 /** static class logger */ 054 private static final Logger LOG = LoggerFactory.getLogger( DefaultAttributeTypeRegistry.class ); 055 056 /** cached Oid/normalizer mapping */ 057 private Map<String, OidNormalizer> oidNormalizerMap; 058 059 /** maps OIDs to a Set of descendants for that OID */ 060 private Map<String, Set<AttributeType>> oidToDescendantSet; 061 062 063 /** 064 * Creates a new default AttributeTypeRegistry instance. 065 */ 066 public DefaultAttributeTypeRegistry() 067 { 068 super( SchemaObjectType.ATTRIBUTE_TYPE, new OidRegistry<AttributeType>() ); 069 oidNormalizerMap = new HashMap<>(); 070 oidToDescendantSet = new HashMap<>(); 071 } 072 073 074 /** 075 * {@inheritDoc} 076 */ 077 @Override 078 public Map<String, OidNormalizer> getNormalizerMapping() 079 { 080 return Collections.unmodifiableMap( oidNormalizerMap ); 081 } 082 083 084 /** 085 * {@inheritDoc} 086 */ 087 @Override 088 public boolean hasDescendants( String ancestorId ) throws LdapException 089 { 090 try 091 { 092 String oid = getOidByName( ancestorId ); 093 Set<AttributeType> descendants = oidToDescendantSet.get( oid ); 094 return ( descendants != null ) && !descendants.isEmpty(); 095 } 096 catch ( LdapException ne ) 097 { 098 throw new LdapNoSuchAttributeException( ne.getMessage(), ne ); 099 } 100 } 101 102 103 /** 104 * {@inheritDoc} 105 */ 106 @Override 107 public boolean hasDescendants( AttributeType ancestor ) throws LdapException 108 { 109 String oid = ancestor.getOid(); 110 Set<AttributeType> descendants = oidToDescendantSet.get( oid ); 111 return ( descendants != null ) && !descendants.isEmpty(); 112 } 113 114 115 /** 116 * {@inheritDoc} 117 */ 118 @SuppressWarnings("unchecked") 119 @Override 120 public Iterator<AttributeType> descendants( String ancestorId ) throws LdapException 121 { 122 try 123 { 124 String oid = getOidByName( ancestorId ); 125 Set<AttributeType> descendants = oidToDescendantSet.get( oid ); 126 127 if ( descendants == null ) 128 { 129 return Collections.EMPTY_SET.iterator(); 130 } 131 132 return descendants.iterator(); 133 } 134 catch ( LdapException ne ) 135 { 136 throw new LdapNoSuchAttributeException( ne.getMessage(), ne ); 137 } 138 } 139 140 141 /** 142 * {@inheritDoc} 143 */ 144 @SuppressWarnings("unchecked") 145 @Override 146 public Iterator<AttributeType> descendants( AttributeType ancestor ) throws LdapException 147 { 148 String oid = ancestor.getOid(); 149 Set<AttributeType> descendants = oidToDescendantSet.get( oid ); 150 151 if ( descendants == null ) 152 { 153 return Collections.EMPTY_SET.iterator(); 154 } 155 156 return descendants.iterator(); 157 } 158 159 160 /** 161 * {@inheritDoc} 162 */ 163 @Override 164 public void registerDescendants( AttributeType attributeType, AttributeType ancestor ) throws LdapException 165 { 166 // add this attribute to descendant list of other attributes in superior chain 167 if ( ancestor == null ) 168 { 169 return; 170 } 171 172 // Check if the ancestor is not the attribute type itself 173 if ( attributeType.getOid().equals( ancestor.getOid() ) ) 174 { 175 throw new LdapSchemaException( LdapSchemaExceptionCodes.AT_CYCLE_TYPE_HIERARCHY ); 176 } 177 178 // Get the ancestor's descendant, if any 179 Set<AttributeType> descendants = oidToDescendantSet.get( ancestor.getOid() ); 180 181 // Initialize the descendant Set to store the descendants for the attributeType 182 if ( descendants == null ) 183 { 184 descendants = new HashSet<>( 1 ); 185 oidToDescendantSet.put( ancestor.getOid(), descendants ); 186 } 187 188 // Add the current type as a descendant 189 descendants.add( attributeType ); 190 } 191 192 193 /** 194 * {@inheritDoc} 195 */ 196 @Override 197 public void unregisterDescendants( AttributeType attributeType, AttributeType ancestor ) throws LdapException 198 { 199 // add this attribute to descendant list of other attributes in superior chain 200 if ( ancestor == null ) 201 { 202 return; 203 } 204 205 // Get the ancestor's descendant, if any 206 Set<AttributeType> descendants = oidToDescendantSet.get( ancestor.getOid() ); 207 208 if ( descendants != null ) 209 { 210 descendants.remove( attributeType ); 211 212 if ( descendants.isEmpty() ) 213 { 214 oidToDescendantSet.remove( ancestor.getOid() ); 215 } 216 } 217 } 218 219 220 /** 221 * {@inheritDoc} 222 */ 223 @Override 224 public AttributeType unregister( String numericOid ) throws LdapException 225 { 226 try 227 { 228 AttributeType removed = super.unregister( numericOid ); 229 230 removeMappingFor( removed ); 231 232 // Deleting an AT which might be used as a superior means we have 233 // to recursively update the descendant map. We also have to remove 234 // the at.oid -> descendant relation 235 oidToDescendantSet.remove( numericOid ); 236 237 // Now recurse if needed 238 unregisterDescendants( removed, removed.getSuperior() ); 239 240 return removed; 241 } 242 catch ( LdapException ne ) 243 { 244 throw new LdapNoSuchAttributeException( ne.getMessage(), ne ); 245 } 246 } 247 248 249 /** 250 * {@inheritDoc} 251 */ 252 @Override 253 public void addMappingFor( AttributeType attributeType ) throws LdapException 254 { 255 MatchingRule equality = attributeType.getEquality(); 256 OidNormalizer oidNormalizer; 257 String oid = attributeType.getOid(); 258 259 if ( equality == null ) 260 { 261 if ( LOG.isDebugEnabled() ) 262 { 263 LOG.debug( I18n.msg( I18n.MSG_13703_AT_WITHOUT_EQ_MR, attributeType.getName() ) ); 264 } 265 266 oidNormalizer = new OidNormalizer( oid, new NoOpNormalizer( attributeType.getOid() ) ); 267 } 268 else 269 { 270 oidNormalizer = new OidNormalizer( oid, equality.getNormalizer() ); 271 } 272 273 oidNormalizerMap.put( oid, oidNormalizer ); 274 275 // Also inject the attributeType's short names in the map 276 for ( String name : attributeType.getNames() ) 277 { 278 oidNormalizerMap.put( Strings.toLowerCaseAscii( name ), oidNormalizer ); 279 } 280 } 281 282 283 /** 284 * Remove the AttributeType normalizer from the OidNormalizer map 285 */ 286 @Override 287 public void removeMappingFor( AttributeType attributeType ) throws LdapException 288 { 289 if ( attributeType == null ) 290 { 291 return; 292 } 293 294 oidNormalizerMap.remove( attributeType.getOid() ); 295 296 // We also have to remove all the short names for this attribute 297 for ( String name : attributeType.getNames() ) 298 { 299 oidNormalizerMap.remove( Strings.toLowerCaseAscii( name ) ); 300 } 301 } 302 303 304 /** 305 * {@inheritDoc} 306 */ 307 @Override 308 public AttributeType lookup( String oid ) throws LdapException 309 { 310 try 311 { 312 return super.lookup( oid ); 313 } 314 catch ( LdapException ne ) 315 { 316 throw new LdapNoSuchAttributeException( ne.getMessage(), ne ); 317 } 318 } 319 320 321 /** 322 * {@inheritDoc} 323 */ 324 @Override 325 public DefaultAttributeTypeRegistry copy() 326 { 327 DefaultAttributeTypeRegistry copy = new DefaultAttributeTypeRegistry(); 328 329 // Copy the base data 330 copy.copy( this ); 331 332 return copy; 333 } 334 335 336 /** 337 * {@inheritDoc} 338 */ 339 @Override 340 public void clear() 341 { 342 // First clear the shared elements 343 super.clear(); 344 345 // clear the OidNormalizer map 346 oidNormalizerMap.clear(); 347 348 // and clear the descendant 349 for ( Map.Entry<String, Set<AttributeType>> entry : oidToDescendantSet.entrySet() ) 350 { 351 Set<AttributeType> descendants = entry.getValue(); 352 353 if ( descendants != null ) 354 { 355 descendants.clear(); 356 } 357 } 358 359 oidToDescendantSet.clear(); 360 } 361}