blob: 932372fc4df2963aec6589333ee8e03f9d4b9408 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.directory.studio.ldapbrowser.core.model.schema;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.directory.api.ldap.model.constants.SchemaConstants;
import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.directory.api.ldap.model.schema.AttributeType;
import org.apache.directory.api.ldap.model.schema.LdapSyntax;
import org.apache.directory.api.ldap.model.schema.MatchingRule;
import org.apache.directory.api.ldap.model.schema.MatchingRuleUse;
import org.apache.directory.api.ldap.model.schema.ObjectClass;
import org.apache.directory.api.ldap.model.schema.UsageEnum;
import org.apache.directory.api.ldap.model.schema.parsers.AttributeTypeDescriptionSchemaParser;
import org.apache.directory.api.ldap.model.schema.parsers.LdapSyntaxDescriptionSchemaParser;
import org.apache.directory.api.ldap.model.schema.parsers.MatchingRuleDescriptionSchemaParser;
import org.apache.directory.api.ldap.model.schema.parsers.MatchingRuleUseDescriptionSchemaParser;
import org.apache.directory.api.ldap.model.schema.parsers.ObjectClassDescriptionSchemaParser;
import org.apache.directory.api.util.Strings;
import org.apache.directory.studio.connection.core.Utils;
import org.apache.directory.studio.ldapbrowser.core.model.AttributeDescription;
import org.apache.directory.studio.ldifparser.LdifFormatParameters;
import org.apache.directory.studio.ldifparser.model.LdifEnumeration;
import org.apache.directory.studio.ldifparser.model.container.LdifContainer;
import org.apache.directory.studio.ldifparser.model.container.LdifContentRecord;
import org.apache.directory.studio.ldifparser.model.lines.LdifAttrValLine;
import org.apache.directory.studio.ldifparser.parser.LdifParser;
/**
* The schema is the central access point to all schema information.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class Schema
{
public static final String SCHEMA_FILTER = "(objectClass=subschema)"; //$NON-NLS-1$
public static final String RAW_SCHEMA_DEFINITION_LDIF_VALUE = "X-RAW-SCHEMA-DEFINITION"; //$NON-NLS-1$
public static final String DN_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.12"; //$NON-NLS-1$
public static final LdapSyntax DUMMY_LDAP_SYNTAX;
static
{
DUMMY_LDAP_SYNTAX = new LdapSyntax( "", "" ); //$NON-NLS-1$ //$NON-NLS-2$
}
public static final HashMap<String, List<String>> DUMMY_EXTENSIONS;
static
{
DUMMY_EXTENSIONS = new HashMap<String, List<String>>();
List<String> dummyValues = new ArrayList<String>();
dummyValues.add( "DUMMY" ); //$NON-NLS-1$
DUMMY_EXTENSIONS.put( "X-DUMMY", dummyValues ); //$NON-NLS-1$
}
public static final Schema DEFAULT_SCHEMA;
static
{
Schema defaultSchema = null;
try
{
URL url = Schema.class.getClassLoader().getResource( "default_schema.ldif" ); //$NON-NLS-1$
InputStream is = url.openStream();
Reader reader = new InputStreamReader( is );
defaultSchema = new Schema();
defaultSchema.defaultSchema = true;
defaultSchema.loadFromLdif( reader );
}
catch ( Exception e )
{
e.printStackTrace();
}
DEFAULT_SCHEMA = defaultSchema;
}
private boolean defaultSchema = false;
public boolean isDefault()
{
return this.defaultSchema;
}
private LdifContentRecord schemaRecord;
private Dn dn;
private String createTimestamp;
private String modifyTimestamp;
private Map<String, ObjectClass> ocdMapByNameOrNumericOid;
private Map<String, AttributeType> atdMapByNameOrNumericOid;
private Map<String, LdapSyntax> lsdMapByNumericOid;
private Map<String, MatchingRule> mrdMapByNameOrNumericOid;
private Map<String, MatchingRuleUse> mrudMapByNameOrNumericOid;
/**
* Creates a new instance of Schema.
*/
public Schema()
{
this.schemaRecord = null;
this.dn = null;
this.createTimestamp = null;
this.modifyTimestamp = null;
this.ocdMapByNameOrNumericOid = new HashMap<String, ObjectClass>();
this.atdMapByNameOrNumericOid = new HashMap<String, AttributeType>();
this.lsdMapByNumericOid = new HashMap<String, LdapSyntax>();
this.mrdMapByNameOrNumericOid = new HashMap<String, MatchingRule>();
this.mrudMapByNameOrNumericOid = new HashMap<String, MatchingRuleUse>();
}
/**
* Loads all schema elements from the given reader. The input must be in
* LDIF format.
*
* @param reader the reader
*/
public void loadFromLdif( Reader reader )
{
try
{
LdifParser parser = new LdifParser();
LdifEnumeration enumeration = parser.parse( reader );
while ( enumeration.hasNext() )
{
LdifContainer container = enumeration.next();
if ( container instanceof LdifContentRecord )
{
LdifContentRecord schemaRecord = ( LdifContentRecord ) container;
parseSchemaRecord( schemaRecord );
}
}
}
catch ( Exception e )
{
// TODO: exception handling
System.out.println( "Schema#loadFromLdif: " + e.toString() ); //$NON-NLS-1$
}
}
/**
* Load all schema elements from the given schema record.
*
* @param schemaRecord the schema record
*/
public void loadFromRecord( LdifContentRecord schemaRecord )
{
try
{
parseSchemaRecord( schemaRecord );
}
catch ( Exception e )
{
// TODO: exception handling
System.out.println( "Schema#loadFromRecord: " + e.toString() ); //$NON-NLS-1$
}
}
/**
* Saves the schema in LDIF format to the given writer.
*
* @param writer
*/
public void saveToLdif( Writer writer )
{
try
{
writer.write( getSchemaRecord().toFormattedString( LdifFormatParameters.DEFAULT ) );
}
catch ( Exception e )
{
// TODO: exception handling
System.out.println( "Schema#saveToLdif: " + e.toString() ); //$NON-NLS-1$
}
}
/**
* Parses the schema record.
*
* @param schemaRecord the schema record
*
* @throws Exception the exception
*/
private void parseSchemaRecord( LdifContentRecord schemaRecord ) throws Exception
{
setSchemaRecord( schemaRecord );
setDn( new Dn( schemaRecord.getDnLine().getValueAsString() ) );
ObjectClassDescriptionSchemaParser ocdPparser = new ObjectClassDescriptionSchemaParser();
ocdPparser.setQuirksMode( true );
AttributeTypeDescriptionSchemaParser atdParser = new AttributeTypeDescriptionSchemaParser();
atdParser.setQuirksMode( true );
LdapSyntaxDescriptionSchemaParser lsdParser = new LdapSyntaxDescriptionSchemaParser();
lsdParser.setQuirksMode( true );
MatchingRuleDescriptionSchemaParser mrdParser = new MatchingRuleDescriptionSchemaParser();
mrdParser.setQuirksMode( true );
MatchingRuleUseDescriptionSchemaParser mrudParser = new MatchingRuleUseDescriptionSchemaParser();
mrudParser.setQuirksMode( true );
LdifAttrValLine[] lines = schemaRecord.getAttrVals();
for ( int i = 0; i < lines.length; i++ )
{
LdifAttrValLine line = lines[i];
String attributeName = line.getUnfoldedAttributeDescription();
String value = line.getValueAsString();
List<String> ldifValues = new ArrayList<String>( 1 );
ldifValues.add( value );
try
{
if ( attributeName.equalsIgnoreCase( SchemaConstants.OBJECT_CLASSES_AT ) )
{
ObjectClass ocd = ocdPparser.parse( value );
ocd.addExtension( RAW_SCHEMA_DEFINITION_LDIF_VALUE, ldifValues );
addObjectClass( ocd );
}
else if ( attributeName.equalsIgnoreCase( SchemaConstants.ATTRIBUTE_TYPES_AT ) )
{
AttributeType atd = atdParser.parse( value );
atd.addExtension( RAW_SCHEMA_DEFINITION_LDIF_VALUE, ldifValues );
addAttributeType( atd );
}
else if ( attributeName.equalsIgnoreCase( SchemaConstants.LDAP_SYNTAXES_AT ) )
{
LdapSyntax lsd = lsdParser.parse( value );
if ( StringUtils.isEmpty( lsd.getDescription() )
&& Utils.getOidDescription( lsd.getOid() ) != null )
{
lsd.setDescription( Utils.getOidDescription( lsd.getOid() ) );
}
lsd.addExtension( RAW_SCHEMA_DEFINITION_LDIF_VALUE, ldifValues );
addLdapSyntax( lsd );
}
else if ( attributeName.equalsIgnoreCase( SchemaConstants.MATCHING_RULES_AT ) )
{
MatchingRule mrd = mrdParser.parse( value );
mrd.addExtension( RAW_SCHEMA_DEFINITION_LDIF_VALUE, ldifValues );
addMatchingRule( mrd );
}
else if ( attributeName.equalsIgnoreCase( SchemaConstants.MATCHING_RULE_USE_AT ) )
{
MatchingRuleUse mrud = mrudParser.parse( value );
mrud.addExtension( RAW_SCHEMA_DEFINITION_LDIF_VALUE, ldifValues );
addMatchingRuleUse( mrud );
}
else if ( attributeName.equalsIgnoreCase( SchemaConstants.CREATE_TIMESTAMP_AT ) )
{
setCreateTimestamp( value );
}
else if ( attributeName.equalsIgnoreCase( SchemaConstants.MODIFY_TIMESTAMP_AT ) )
{
setModifyTimestamp( value );
}
}
catch ( Exception e )
{
// TODO: exception handling
System.out.println( "Error reading schema: " + attributeName + " = " + value ); //$NON-NLS-1$ //$NON-NLS-2$
System.out.println( e.getMessage() );
}
}
for ( AttributeType atd : getAttributeTypeDescriptions() )
{
// assume all received syntaxes in attributes are valid -> create pseudo syntaxes if missing
String syntaxOid = atd.getSyntaxOid();
if ( syntaxOid != null && !hasLdapSyntaxDescription( syntaxOid ) )
{
LdapSyntax lsd = new LdapSyntax( syntaxOid );
lsd.setDescription( Utils.getOidDescription( syntaxOid ) );
addLdapSyntax( lsd );
}
// assume all received matching rules in attributes are valid -> create pseudo matching rules if missing
String emr = atd.getEqualityOid();
String omr = atd.getOrderingOid();
String smr = atd.getSubstringOid();
checkMatchingRules( emr, omr, smr );
}
// set extensibleObject may attributes
ObjectClass extensibleObjectOcd = this.getObjectClassDescription( SchemaConstants.EXTENSIBLE_OBJECT_OC );
Collection<AttributeType> userAtds = SchemaUtils.getUserAttributeDescriptions( this );
Collection<String> atdNames = SchemaUtils.getNames( userAtds );
List<String> atdNames2 = new ArrayList<String>( atdNames );
extensibleObjectOcd.setMayAttributeTypeOids( atdNames2 );
}
private void checkMatchingRules( String... matchingRules )
{
for ( String matchingRule : matchingRules )
{
if ( matchingRule != null && !hasMatchingRuleDescription( matchingRule ) )
{
MatchingRule mrd = new MatchingRule( matchingRule );
mrd.addName( matchingRule );
addMatchingRule( mrd );
}
}
}
/**
* Gets the schema record.
*
* @return the schema record when the schema was created using the
* loadFromLdif() method, null otherwise
*/
public LdifContentRecord getSchemaRecord()
{
return schemaRecord;
}
/**
* Sets the schema record.
*
* @param schemaRecord the new schema record
*/
public void setSchemaRecord( LdifContentRecord schemaRecord )
{
this.schemaRecord = schemaRecord;
}
/**
* Gets the Dn of the schema record, may be null.
*
* @return the Dn of the schema record, may be null
*/
public Dn getDn()
{
return dn;
}
/**
* Sets the Dn.
*
* @param dn the new Dn
*/
public void setDn( Dn dn )
{
this.dn = dn;
}
/**
* Gets the create timestamp of the schema record, may be null.
*
* @return the create timestamp of the schema record, may be null
*/
public String getCreateTimestamp()
{
return createTimestamp;
}
/**
* Sets the creates the timestamp.
*
* @param createTimestamp the new creates the timestamp
*/
public void setCreateTimestamp( String createTimestamp )
{
this.createTimestamp = createTimestamp;
}
/**
* Gets the modify timestamp of the schema record, may be null.
*
* @return the modify timestamp of the schema record, may be null
*/
public String getModifyTimestamp()
{
return modifyTimestamp;
}
/**
* Sets the modify timestamp.
*
* @param modifyTimestamp the new modify timestamp
*/
public void setModifyTimestamp( String modifyTimestamp )
{
this.modifyTimestamp = modifyTimestamp;
}
////////////////////// Object Class Description //////////////////////
/**
* Adds the object class description.
*
* @param ocd the object class description
*/
private void addObjectClass( ObjectClass ocd )
{
if ( ocd.getOid() != null )
{
ocdMapByNameOrNumericOid.put( Strings.toLowerCase( ocd.getOid() ), ocd );
}
if ( ocd.getNames() != null && !ocd.getNames().isEmpty() )
{
for ( String ocdName : ocd.getNames() )
{
ocdMapByNameOrNumericOid.put( Strings.toLowerCase( ocdName ), ocd );
}
}
}
/**
* Gets the object class descriptions.
*
* @return the object class descriptions
*/
public Collection<ObjectClass> getObjectClassDescriptions()
{
Set<ObjectClass> set = new HashSet<ObjectClass>( ocdMapByNameOrNumericOid.values() );
return set;
}
/**
* Checks if an object class descriptions with the given name or OID exists.
*
* @param nameOrOid the name numeric OID of the object class description
*
* @return true if an object class description with the given name
* or OID exists.
*/
public boolean hasObjectClassDescription( String nameOrOid )
{
if ( nameOrOid != null )
{
return ocdMapByNameOrNumericOid.containsKey( Strings.toLowerCase( nameOrOid ) );
}
return false;
}
/**
* Returns the object class description of the given name. If no such
* object exists the default or a dummy object class description is
* returned.
*
* @param nameOrOid the name numeric OID of the object class description
*
* @return the object class description, or the default or a dummy
*/
public ObjectClass getObjectClassDescription( String nameOrOid )
{
if ( ocdMapByNameOrNumericOid.containsKey( Strings.toLowerCase( nameOrOid ) ) )
{
return ocdMapByNameOrNumericOid.get( Strings.toLowerCase( nameOrOid ) );
}
else if ( !isDefault() )
{
return DEFAULT_SCHEMA.getObjectClassDescription( nameOrOid );
}
else
{
// DUMMY
List<String> names = new ArrayList<String>();
names.add( nameOrOid );
ObjectClass ocd = new ObjectClass( nameOrOid );
ocd.setNames( names );
ocd.setExtensions( DUMMY_EXTENSIONS );
return ocd;
}
}
////////////////////// Attribute Type Description //////////////////////
/**
* Adds the attribute type description.
*
* @param atd the attribute type description
*/
private void addAttributeType( AttributeType atd )
{
if ( atd.getOid() != null )
{
atdMapByNameOrNumericOid.put( Strings.toLowerCase( atd.getOid() ), atd );
}
if ( atd.getNames() != null && !atd.getNames().isEmpty() )
{
for ( String atdName : atd.getNames() )
{
atdMapByNameOrNumericOid.put( Strings.toLowerCase( atdName ), atd );
}
}
}
/**
* Gets the attribute type descriptions.
*
* @return the attribute type descriptions
*/
public Collection<AttributeType> getAttributeTypeDescriptions()
{
Set<AttributeType> set = new HashSet<AttributeType>( atdMapByNameOrNumericOid.values() );
return set;
}
/**
* Checks if an attribute type descriptions with the given name or OID exists.
*
* @param nameOrOid the name numeric OID of the attribute type description
*
* @return true if an attribute type description with the given name
* or OID exists.
*/
public boolean hasAttributeTypeDescription( String nameOrOid )
{
if ( nameOrOid != null )
{
return atdMapByNameOrNumericOid.containsKey( Strings.toLowerCase( nameOrOid ) );
}
return false;
}
/**
* Returns the attribute type description of the given name. If no such
* object exists the default or a dummy attribute type description is
* returned.
*
* @param nameOrOid the name numeric OID of the attribute type description
*
* @return the attribute type description, or the default or a dummy
*/
public AttributeType getAttributeTypeDescription( String nameOrOid )
{
AttributeDescription ad = new AttributeDescription( nameOrOid );
String attributeType = ad.getParsedAttributeType();
if ( atdMapByNameOrNumericOid.containsKey( Strings.toLowerCase( attributeType ) ) )
{
return atdMapByNameOrNumericOid.get( Strings.toLowerCase( attributeType ) );
}
else if ( !isDefault() )
{
return DEFAULT_SCHEMA.getAttributeTypeDescription( attributeType );
}
else
{
// DUMMY
List<String> attributeTypes = new ArrayList<String>();
attributeTypes.add( attributeType );
AttributeType atd = new AttributeType( attributeType );
atd.setNames( attributeTypes );
atd.setUserModifiable( true );
atd.setUsage( UsageEnum.USER_APPLICATIONS );
atd.setExtensions( DUMMY_EXTENSIONS );
return atd;
}
}
//////////////////////// LDAP Syntax Description ////////////////////////
/**
* Adds the LDAP syntax description.
*
* @param lsd the LDAP syntax description
*/
private void addLdapSyntax( LdapSyntax lsd )
{
if ( lsd.getOid() != null )
{
lsdMapByNumericOid.put( Strings.toLowerCase( lsd.getOid() ), lsd );
}
}
/**
* Gets the LDAP syntax descriptions.
*
* @return the LDAP syntax descriptions
*/
public Collection<LdapSyntax> getLdapSyntaxDescriptions()
{
Set<LdapSyntax> set = new HashSet<LdapSyntax>( lsdMapByNumericOid.values() );
return set;
}
/**
* Checks if an LDAP syntax descriptions with the given OID exists.
*
* @param numericOid the numeric OID of the LDAP syntax description
*
* @return true if an LDAP syntax description with the given OID exists.
*/
public boolean hasLdapSyntaxDescription( String numericOid )
{
if ( numericOid != null )
{
return lsdMapByNumericOid.containsKey( Strings.toLowerCase( numericOid ) );
}
return false;
}
/**
* Returns the syntax description of the given OID. If no such object
* exists the default or a dummy syntax description is returned.
*
* @param numericOid the numeric OID of the LDAP syntax description
*
* @return the attribute type description or the default or a dummy
*/
public LdapSyntax getLdapSyntaxDescription( String numericOid )
{
if ( numericOid == null )
{
return DUMMY_LDAP_SYNTAX;
}
else if ( lsdMapByNumericOid.containsKey( Strings.toLowerCase( numericOid ) ) )
{
return lsdMapByNumericOid.get( Strings.toLowerCase( numericOid ) );
}
else if ( !isDefault() )
{
return DEFAULT_SCHEMA.getLdapSyntaxDescription( numericOid );
}
else
{
// DUMMY
LdapSyntax lsd = new LdapSyntax( numericOid );
lsd.setExtensions( DUMMY_EXTENSIONS );
return lsd;
}
}
////////////////////////// Matching Rule Description //////////////////////////
/**
* Adds the matching rule description.
*
* @param mrud the matching rule description
*/
private void addMatchingRule( MatchingRule mrd )
{
if ( mrd.getOid() != null )
{
mrdMapByNameOrNumericOid.put( Strings.toLowerCase( mrd.getOid() ), mrd );
}
if ( mrd.getNames() != null && !mrd.getNames().isEmpty() )
{
for ( String mrdName : mrd.getNames() )
{
mrdMapByNameOrNumericOid.put( Strings.toLowerCase( mrdName ), mrd );
}
}
}
/**
* Gets the matching rule descriptions.
*
* @return the matching rule descriptions
*/
public Collection<MatchingRule> getMatchingRuleDescriptions()
{
Set<MatchingRule> set = new HashSet<MatchingRule>( mrdMapByNameOrNumericOid.values() );
return set;
}
/**
* Checks if an matching rule descriptions with the given name or OID exists.
*
* @param nameOrOid the name numeric OID of the matching rule description
*
* @return true if a matching rule description with the given name
* or OID exists.
*/
public boolean hasMatchingRuleDescription( String nameOrOid )
{
if ( nameOrOid != null )
{
return mrdMapByNameOrNumericOid.containsKey( Strings.toLowerCase( nameOrOid ) );
}
return false;
}
/**
* Returns the matching rule description of the given name or OID. If no
* such object exists the default or a dummy matching rule description
* is returned.
*
* @param nameOrOid the name or numeric OID of the matching rule description
*
* @return the matching rule description or the default or a dummy
*/
public MatchingRule getMatchingRuleDescription( String nameOrOid )
{
if ( mrdMapByNameOrNumericOid.containsKey( Strings.toLowerCase( nameOrOid ) ) )
{
return mrdMapByNameOrNumericOid.get( Strings.toLowerCase( nameOrOid ) );
}
else if ( !isDefault() )
{
return DEFAULT_SCHEMA.getMatchingRuleDescription( nameOrOid );
}
else
{
// DUMMY
MatchingRule mrd = new MatchingRule( nameOrOid );
mrd.setExtensions( DUMMY_EXTENSIONS );
return mrd;
}
}
//////////////////////// Matching Rule Use Description ////////////////////////
/**
* Adds the matching rule use description.
*
* @param mrud the matching rule use description
*/
private void addMatchingRuleUse( MatchingRuleUse mrud )
{
if ( mrud.getOid() != null )
{
mrudMapByNameOrNumericOid.put( Strings.toLowerCase( mrud.getOid() ), mrud );
}
if ( mrud.getNames() != null && !mrud.getNames().isEmpty() )
{
for ( String mrudName : mrud.getNames() )
{
mrudMapByNameOrNumericOid.put( Strings.toLowerCase( mrudName ), mrud );
}
}
}
/**
* Gets the matching rule use descriptions.
*
* @return the matching rule use descriptions
*/
public Collection<MatchingRuleUse> getMatchingRuleUseDescriptions()
{
Set<MatchingRuleUse> set = new HashSet<MatchingRuleUse>( mrudMapByNameOrNumericOid
.values() );
return set;
}
/**
* Checks if an matching rule use descriptions with the given name or OID exists.
*
* @param nameOrOid the name numeric OID of the matching rule use description
*
* @return true if a matching rule use description with the given name
* or OID exists.
*/
public boolean hasMatchingRuleUseDescription( String nameOrOid )
{
if ( nameOrOid != null )
{
return mrudMapByNameOrNumericOid.containsKey( Strings.toLowerCase( nameOrOid ) );
}
return false;
}
/**
* Returns the matching rule use description of the given name or OID. If no
* such object exists the default or a dummy matching rule use description
* is returned.
*
* @param nameOrOid the name or numeric OID of the matching rule use description
*
* @return the matching rule use description or the default or a dummy
*/
public MatchingRuleUse getMatchingRuleUseDescription( String nameOrOid )
{
if ( mrudMapByNameOrNumericOid.containsKey( Strings.toLowerCase( nameOrOid ) ) )
{
return mrudMapByNameOrNumericOid.get( Strings.toLowerCase( nameOrOid ) );
}
else if ( !isDefault() )
{
return DEFAULT_SCHEMA.getMatchingRuleUseDescription( nameOrOid );
}
else
{
// DUMMY
MatchingRuleUse mrud = new MatchingRuleUse( nameOrOid );
mrud.setExtensions( DUMMY_EXTENSIONS );
return mrud;
}
}
}