| /* |
| * 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; |
| |
| |
| import java.io.Serializable; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.apache.directory.api.ldap.model.constants.SchemaConstants; |
| import org.apache.directory.api.ldap.model.schema.AttributeType; |
| import org.apache.directory.api.ldap.model.schema.ObjectClass; |
| import org.apache.directory.api.util.Strings; |
| import org.apache.directory.studio.ldapbrowser.core.model.schema.Schema; |
| import org.apache.directory.studio.ldapbrowser.core.model.schema.SchemaUtils; |
| |
| |
| /** |
| * This class implements an attribute description as |
| * specified in RFC4512, section 2.5: |
| * |
| * An attribute description is represented by the ABNF: |
| * |
| * attributedescription = attributetype options |
| * attributetype = oid |
| * options = *( SEMI option ) |
| * option = 1*keychar |
| * |
| * where <attributetype> identifies the attribute type and each <option> |
| * identifies an attribute option. Both <attributetype> and <option> |
| * productions are case insensitive. The order in which <option>s |
| * appear is irrelevant. That is, any two <attributedescription>s that |
| * consist of the same <attributetype> and same set of <option>s are |
| * equivalent. |
| * |
| * Examples of valid attribute descriptions: |
| * |
| * 2.5.4.0 |
| * cn;lang-de;lang-en |
| * owner |
| * |
| * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> |
| */ |
| public class AttributeDescription implements Serializable |
| { |
| |
| private static final long serialVersionUID = 1L; |
| |
| /** The user provided description. */ |
| private String description; |
| |
| /** The parsed attribute type. */ |
| private String parsedAttributeType; |
| |
| /** The parsed language tag option list. */ |
| private List<String> parsedLangList; |
| |
| /** The parsed option list, except the language tags. */ |
| private List<String> parsedOptionList; |
| |
| |
| /** |
| * Creates a new instance of AttributeDescription. |
| * |
| * @param description the user provided description |
| */ |
| public AttributeDescription( String description ) |
| { |
| this.description = description; |
| |
| String[] attributeDescriptionComponents = description.split( IAttribute.OPTION_DELIMITER ); |
| this.parsedAttributeType = attributeDescriptionComponents[0]; |
| this.parsedLangList = new ArrayList<String>(); |
| this.parsedOptionList = new ArrayList<String>(); |
| for ( int i = 1; i < attributeDescriptionComponents.length; i++ ) |
| { |
| String component = attributeDescriptionComponents[i]; |
| if ( component.startsWith( IAttribute.OPTION_LANG_PREFIX ) ) |
| { |
| this.parsedLangList.add( component ); |
| } |
| else |
| { |
| this.parsedOptionList.add( component ); |
| } |
| } |
| } |
| |
| |
| /** |
| * Gets the user provided description. |
| * |
| * @return the user provided description |
| */ |
| public String getDescription() |
| { |
| return description; |
| } |
| |
| |
| /** |
| * Gets the parsed attribute type. |
| * |
| * @return the parsed attribute type |
| */ |
| public String getParsedAttributeType() |
| { |
| return parsedAttributeType; |
| } |
| |
| |
| /** |
| * Gets the list of parsed language tags. |
| * |
| * @return the list of parsed language tags |
| */ |
| public List<String> getParsedLangList() |
| { |
| return parsedLangList; |
| } |
| |
| |
| /** |
| * Gets the list of parsed options, except the language tags. |
| * |
| * @return the list of parsed options, except the language tags |
| */ |
| public List<String> getParsedOptionList() |
| { |
| return parsedOptionList; |
| } |
| |
| |
| /** |
| * Returns the attribute description with the numeric OID |
| * instead of the descriptive attribute type. |
| * |
| * @param schema the schema |
| * |
| * @return the attribute description with the numeric OID |
| */ |
| public String toOidString( Schema schema ) |
| { |
| if ( schema == null ) |
| { |
| return description; |
| } |
| |
| AttributeType atd = schema.getAttributeTypeDescription( parsedAttributeType ); |
| String oidString = atd.getOid(); |
| |
| if ( !parsedLangList.isEmpty() ) |
| { |
| for ( Iterator<String> it = parsedLangList.iterator(); it.hasNext(); ) |
| { |
| String element = it.next(); |
| oidString += element; |
| |
| if ( it.hasNext() || !parsedOptionList.isEmpty() ) |
| { |
| oidString += IAttribute.OPTION_DELIMITER; |
| } |
| } |
| } |
| if ( !parsedOptionList.isEmpty() ) |
| { |
| for ( Iterator<String> it = parsedOptionList.iterator(); it.hasNext(); ) |
| { |
| String element = it.next(); |
| oidString += element; |
| |
| if ( it.hasNext() ) |
| { |
| oidString += IAttribute.OPTION_DELIMITER; |
| } |
| } |
| } |
| |
| return oidString; |
| } |
| |
| |
| /** |
| * Checks if the given attribute description is subtype of |
| * this attribute description. |
| * |
| * @param other the other attribute description |
| * @param schema the schema |
| * |
| * @return true, if the other attribute description is a |
| * subtype of this attribute description. |
| */ |
| public boolean isSubtypeOf( AttributeDescription other, Schema schema ) |
| { |
| // this=name, other=givenName;lang-de -> false |
| // this=name;lang-en, other=givenName;lang-de -> false |
| // this=givenName, other=name -> true |
| // this=givenName;lang-de, other=givenName -> true |
| // this=givenName;lang-de, other=name -> true |
| // this=givenName;lang-en, other=name;lang-de -> false |
| // this=givenName, other=givenName;lang-de -> false |
| |
| // check equal descriptions |
| if ( this.toOidString( schema ).equals( other.toOidString( schema ) ) ) |
| { |
| return false; |
| } |
| |
| AttributeType myAtd = schema.getAttributeTypeDescription( this.getParsedAttributeType() ); |
| AttributeType otherAtd = schema.getAttributeTypeDescription( other.getParsedAttributeType() ); |
| |
| // special case *: all user attributes (RFC4511) |
| if ( SchemaConstants.ALL_USER_ATTRIBUTES.equals( other.description ) && !SchemaUtils.isOperational( myAtd ) ) |
| { |
| return true; |
| } |
| |
| // special case +: all operational attributes (RFC3673) |
| if ( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES.equals( other.description ) |
| && SchemaUtils.isOperational( myAtd ) ) |
| { |
| return true; |
| } |
| |
| // special case @: attributes by object class (RFC4529) |
| if ( other.description.length() > 1 && other.description.startsWith( "@" ) ) //$NON-NLS-1$ |
| { |
| String objectClass = other.description.substring( 1 ); |
| ObjectClass ocd = schema.getObjectClassDescription( objectClass ); |
| ocd.getMayAttributeTypes(); |
| ocd.getMustAttributeTypes(); |
| |
| Collection<String> names = new HashSet<String>(); |
| names.addAll( SchemaUtils.getMayAttributeTypeDescriptionNamesTransitive( ocd, schema ) ); |
| names.addAll( SchemaUtils.getMustAttributeTypeDescriptionNamesTransitive( ocd, schema ) ); |
| for ( String name : names ) |
| { |
| AttributeType atd = schema.getAttributeTypeDescription( name ); |
| if ( myAtd == atd ) |
| { |
| return true; |
| } |
| } |
| } |
| |
| // check type |
| if ( myAtd != otherAtd ) |
| { |
| AttributeType superiorAtd = null; |
| String superiorName = myAtd.getSuperiorOid(); |
| while ( superiorName != null ) |
| { |
| superiorAtd = schema.getAttributeTypeDescription( superiorName ); |
| if ( superiorAtd == otherAtd ) |
| { |
| break; |
| } |
| superiorName = superiorAtd.getSuperiorOid(); |
| } |
| if ( superiorAtd != otherAtd ) |
| { |
| return false; |
| } |
| } |
| |
| // check options |
| List<String> myOptionsList = new ArrayList<String>( this.getParsedOptionList() ); |
| List<String> otherOptionsList = new ArrayList<String>( other.getParsedOptionList() ); |
| otherOptionsList.removeAll( myOptionsList ); |
| if ( !otherOptionsList.isEmpty() ) |
| { |
| return false; |
| } |
| |
| // check language tags |
| List<String> myLangList = new ArrayList<String>( this.getParsedLangList() ); |
| List<String> otherLangList = new ArrayList<String>( other.getParsedLangList() ); |
| for ( String myLang : myLangList ) |
| { |
| for ( Iterator<String> otherIt = otherLangList.iterator(); otherIt.hasNext(); ) |
| { |
| String otherLang = otherIt.next(); |
| if ( otherLang.endsWith( "-" ) ) //$NON-NLS-1$ |
| { |
| if ( Strings.toLowerCase( myLang ).startsWith( Strings.toLowerCase( otherLang ) ) ) |
| { |
| otherIt.remove(); |
| } |
| } |
| else |
| { |
| if ( myLang.equalsIgnoreCase( otherLang ) ) |
| { |
| otherIt.remove(); |
| } |
| } |
| } |
| } |
| if ( !otherLangList.isEmpty() ) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } |