Return-Path: Delivered-To: apmail-directory-commits-archive@www.apache.org Received: (qmail 38593 invoked from network); 5 Aug 2009 17:55:34 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 5 Aug 2009 17:55:34 -0000 Received: (qmail 25399 invoked by uid 500); 5 Aug 2009 17:55:42 -0000 Delivered-To: apmail-directory-commits-archive@directory.apache.org Received: (qmail 25344 invoked by uid 500); 5 Aug 2009 17:55:42 -0000 Mailing-List: contact commits-help@directory.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@directory.apache.org Delivered-To: mailing list commits@directory.apache.org Received: (qmail 25335 invoked by uid 99); 5 Aug 2009 17:55:42 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 05 Aug 2009 17:55:42 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 05 Aug 2009 17:55:38 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 2B216238898D; Wed, 5 Aug 2009 17:55:18 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r801340 [2/13] - in /directory/apacheds/trunk: core-entry/src/main/java/org/apache/directory/server/core/entry/ core-entry/src/test/java/org/apache/directory/server/core/entry/ core-integ/src/main/java/org/apache/directory/server/core/integ... Date: Wed, 05 Aug 2009 17:55:16 -0000 To: commits@directory.apache.org From: elecharny@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20090805175518.2B216238898D@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Modified: directory/apacheds/trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/DefaultServerAttribute.java URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/DefaultServerAttribute.java?rev=801340&r1=801339&r2=801340&view=diff ============================================================================== --- directory/apacheds/trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/DefaultServerAttribute.java (original) +++ directory/apacheds/trunk/core-entry/src/main/java/org/apache/directory/server/core/entry/DefaultServerAttribute.java Wed Aug 5 17:55:15 2009 @@ -1,1423 +1,1423 @@ -/* - * 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.server.core.entry; - - -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; - -import javax.naming.NamingException; -import javax.naming.directory.InvalidAttributeValueException; - -import org.apache.directory.shared.asn1.primitives.OID; -import org.apache.directory.shared.ldap.entry.EntryAttribute; -import org.apache.directory.shared.ldap.entry.Value; -import org.apache.directory.shared.ldap.entry.client.ClientBinaryValue; -import org.apache.directory.shared.ldap.entry.client.ClientStringValue; -import org.apache.directory.shared.ldap.entry.client.DefaultClientAttribute; -import org.apache.directory.shared.ldap.schema.AttributeType; -import org.apache.directory.shared.ldap.util.StringTools; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/** - * A server side entry attribute aware of schema. - * - * @author Apache Directory Project - * @version $Rev$, $Date$ - */ -public final class DefaultServerAttribute extends DefaultClientAttribute implements ServerAttribute -{ - public static final long serialVersionUID = 1L; - - /** logger for reporting errors that might not be handled properly upstream */ - private static final Logger LOG = LoggerFactory.getLogger( DefaultServerAttribute.class ); - - /** The associated AttributeType */ - private AttributeType attributeType; - - - //----------------------------------------------------------------------- - // utility methods - //----------------------------------------------------------------------- - /** - * Private helper method used to set an UpId from an attributeType - * - * @param at The attributeType for which we want the upID - * @return the ID of the given attributeType - */ - private String getUpId( AttributeType at ) - { - String atUpId = at.getName(); - - if ( atUpId == null ) - { - atUpId = at.getOid(); - } - - return atUpId; - } - - - //------------------------------------------------------------------------- - // Constructors - //------------------------------------------------------------------------- - /** - * - * Creates a new instance of DefaultServerAttribute, by copying - * another attribute, which can be a ClientAttribute. If the other - * attribute is a ServerAttribute, it will be copied. - * - * @param attributeType The attribute's type - * @param attribute The attribute to be copied - */ - public DefaultServerAttribute( AttributeType attributeType, EntryAttribute attribute ) - { - // Copy the common values. isHR is only available on a ServerAttribute - this.attributeType = attributeType; - this.id = attribute.getId(); - this.upId = attribute.getUpId(); - - if ( attribute instanceof ServerAttribute ) - { - isHR = attribute.isHR(); - - // Copy all the values - for ( Value value:attribute ) - { - add( value.clone() ); - } - } - else - { - - try - { - isHR = attributeType.getSyntax().isHumanReadable(); - } - catch ( NamingException ne ) - { - // Do nothing : the syntax should always exist ... - } - - - // Copy all the values - for ( Value clientValue:attribute ) - { - Value serverValue = null; - - // We have to convert the value first - if ( clientValue instanceof ClientStringValue ) - { - if ( isHR ) - { - serverValue = new ServerStringValue( attributeType, clientValue.getString() ); - } - else - { - // We have to convert the value to a binary value first - serverValue = new ServerBinaryValue( attributeType, - clientValue.getBytes() ); - } - } - else if ( clientValue instanceof ClientBinaryValue ) - { - if ( isHR ) - { - // We have to convert the value to a String value first - serverValue = new ServerStringValue( attributeType, - clientValue.getString() ); - } - else - { - serverValue = new ServerBinaryValue( attributeType, clientValue.getBytes() ); - } - } - - add( serverValue ); - } - } - } - - - // maybe have some additional convenience constructors which take - // an initial value as a string or a byte[] - /** - * Create a new instance of a EntryAttribute, without ID nor value. - * - * @param attributeType the attributeType for the empty attribute added into the entry - */ - public DefaultServerAttribute( AttributeType attributeType ) - { - if ( attributeType == null ) - { - throw new IllegalArgumentException( "The AttributeType parameter should not be null" ); - } - - setAttributeType( attributeType ); - } - - - /** - * Create a new instance of a EntryAttribute, without value. - * - * @param upId the ID for the added attributeType - * @param attributeType the added AttributeType - */ - public DefaultServerAttribute( String upId, AttributeType attributeType ) - { - if ( attributeType == null ) - { - String message = "The AttributeType parameter should not be null"; - LOG.error( message ); - throw new IllegalArgumentException( message ); - } - - setAttributeType( attributeType ); - setUpId( upId ); - } - - - /** - * Doc me more! - * - * If the value does not correspond to the same attributeType, then it's - * wrapped value is copied into a new Value which uses the specified - * attributeType. - * - * @param attributeType the attribute type according to the schema - * @param vals an initial set of values for this attribute - */ - public DefaultServerAttribute( AttributeType attributeType, Value... vals ) - { - this( null, attributeType, vals ); - } - - - /** - * Doc me more! - * - * If the value does not correspond to the same attributeType, then it's - * wrapped value is copied into a new Value which uses the specified - * attributeType. - * - * Otherwise, the value is stored, but as a reference. It's not a copy. - * - * @param upId the ID of the added attribute - * @param attributeType the attribute type according to the schema - * @param vals an initial set of values for this attribute - */ - public DefaultServerAttribute( String upId, AttributeType attributeType, Value... vals ) - { - if ( attributeType == null ) - { - throw new IllegalArgumentException( "The AttributeType parameter should not be null" ); - } - - setAttributeType( attributeType ); - setUpId( upId, attributeType ); - add( vals ); - } - - - /** - * Create a new instance of a EntryAttribute, without ID but with some values. - * - * @param attributeType The attributeType added on creation - * @param vals The added value for this attribute - */ - public DefaultServerAttribute( AttributeType attributeType, String... vals ) - { - this( null, attributeType, vals ); - } - - - /** - * Create a new instance of a EntryAttribute. - * - * @param upId the ID for the added attribute - * @param attributeType The attributeType added on creation - * @param vals the added values for this attribute - */ - public DefaultServerAttribute( String upId, AttributeType attributeType, String... vals ) - { - if ( attributeType == null ) - { - throw new IllegalArgumentException( "The AttributeType parameter should not be null" ); - } - - setAttributeType( attributeType ); - add( vals ); - setUpId( upId, attributeType ); - } - - - /** - * Create a new instance of a EntryAttribute, with some byte[] values. - * - * @param attributeType The attributeType added on creation - * @param vals The value for the added attribute - */ - public DefaultServerAttribute( AttributeType attributeType, byte[]... vals ) - { - this( null, attributeType, vals ); - } - - - /** - * Create a new instance of a EntryAttribute, with some byte[] values. - * - * @param upId the ID for the added attribute - * @param attributeType the AttributeType to be added - * @param vals the values for the added attribute - */ - public DefaultServerAttribute( String upId, AttributeType attributeType, byte[]... vals ) - { - if ( attributeType == null ) - { - throw new IllegalArgumentException( "The AttributeType parameter should not be null" ); - } - - setAttributeType( attributeType ); - add( vals ); - setUpId( upId, attributeType ); - } - - - //------------------------------------------------------------------------- - // API - //------------------------------------------------------------------------- - /** - *

- * Adds some values to this attribute. If the new values are already present in - * the attribute values, the method has no effect. - *

- *

- * The new values are added at the end of list of values. - *

- *

- * This method returns the number of values that were added. - *

- *

- * If the value's type is different from the attribute's type, - * the value is not added. - *

- * It's the responsibility of the caller to check if the stored - * values are consistent with the attribute's type. - *

- * - * @param vals some new values to be added which may be null - * @return the number of added values, or 0 if none has been added - */ - public int add( byte[]... vals ) - { - if ( !isHR ) - { - int nbAdded = 0; - - for ( byte[] val:vals ) - { - Value value = new ServerBinaryValue( attributeType, val ); - - try - { - value.normalize(); - } - catch( NamingException ne ) - { - // The value can't be normalized : we don't add it. - LOG.error( "The value '" + val + "' can't be normalized, it hasn't been added" ); - return 0; - } - - if ( add( value ) != 0 ) - { - nbAdded++; - } - else - { - LOG.error( "The value '" + val + "' is incorrect, it hasn't been added" ); - } - } - - return nbAdded; - } - else - { - // We can't add Binary values into a String serverAttribute - return 0; - } - } - - - /** - *

- * Adds some values to this attribute. If the new values are already present in - * the attribute values, the method has no effect. - *

- *

- * The new values are added at the end of list of values. - *

- *

- * This method returns the number of values that were added. - *

- * If the value's type is different from the attribute's type, - * the value is not added. - * - * @param vals some new values to be added which may be null - * @return the number of added values, or 0 if none has been added - */ - public int add( String... vals ) - { - if ( isHR ) - { - int nbAdded = 0; - - for ( String val:vals ) - { - Value newValue = new ServerStringValue( attributeType, val ); - - if ( !contains( newValue ) ) - { - if ( add( newValue ) != 0 ) - { - nbAdded++; - } - else - { - LOG.error( "The value '" + val + "' is incorrect, it hasn't been added" ); - } - } - } - - return nbAdded; - } - else - { - // We can't add String values into a Binary serverAttribute - return 0; - } - } - - - /** - * @see EntryAttribute#add(org.apache.directory.shared.ldap.entry.Value...) - * - * @return the number of added values into this attribute - */ - public int add( Value... vals ) - { - int nbAdded = 0; - - for ( Value val:vals ) - { - try - { - if ( attributeType.getSyntax().isHumanReadable() ) - { - if ( ( val == null ) || val.isNull() ) - { - Value nullSV = new ServerStringValue( attributeType, (String)null ); - - if ( !values.contains( nullSV ) ) - { - values.add( nullSV ); - nbAdded++; - } - } - else if ( val instanceof ServerStringValue ) - { - if ( !values.contains( val ) ) - { - if ( values.add( val ) ) - { - nbAdded++; - } - } - } - else if ( val instanceof ClientStringValue ) - { - // If we get a Client value, convert it to a Server value first - Value serverStringValue = new ServerStringValue( attributeType, val.getString() ); - - if ( !values.contains( serverStringValue ) ) - { - if ( values.add( serverStringValue ) ) - { - nbAdded++; - } - } - } - else - { - String message = "The value must be a String, as its AttributeType is H/R"; - LOG.error( message ); - } - } - else - { - if ( val == null ) - { - Value nullSV = new ServerBinaryValue( attributeType, (byte[])null ); - - if ( !values.contains( nullSV ) ) - { - values.add( nullSV ); - nbAdded++; - } - } - else if ( ( val instanceof ClientBinaryValue ) ) - { - Value serverBinaryValue = new ServerBinaryValue( attributeType, val.getBytes() ); - - if ( !values.contains( serverBinaryValue ) ) - { - if ( values.add( serverBinaryValue ) ) - { - nbAdded++; - } - } - } - else if ( val instanceof ServerBinaryValue ) - { - if ( !values.contains( val ) ) - { - if ( values.add( val ) ) - { - nbAdded++; - } - } - } - else - { - String message = "The value must be a byte[], as its AttributeType is not H/R"; - LOG.error( message ); - } - } - } - catch ( NamingException ne ) - { - String message = "Error while adding value '" + val.toString() +"' : " + ne.getMessage(); - LOG.error( message ); - } - } - - return nbAdded; - } - - - /** - * Remove all the values from this attribute type, including a - * null value. - */ - public void clear() - { - values.clear(); - } - - - /** - *

- * Indicates whether all the specified values are attribute's values. If - * at least one value is not an attribute's value, this method will return - * false - *

- *

- * If the Attribute is HR, this method will returns false - *

- * - * @param vals the values - * @return true if this attribute contains all the values, otherwise false - */ - public boolean contains( byte[]... vals ) - { - if ( !isHR ) - { - // Iterate through all the values, and quit if we - // don't find one in the values - for ( byte[] val:vals ) - { - ServerBinaryValue value = new ServerBinaryValue( attributeType, val ); - - try - { - value.normalize(); - } - catch ( NamingException ne ) - { - return false; - } - - if ( !values.contains( value ) ) - { - return false; - } - } - - return true; - } - else - { - return false; - } - } - - - /** - *

- * Indicates whether all the specified values are attribute's values. If - * at least one value is not an attribute's value, this method will return - * false - *

- *

- * If the Attribute is not HR, this method will returns false - *

- * - * @param vals the values - * @return true if this attribute contains all the values, otherwise false - */ - public boolean contains( String... vals ) - { - if ( isHR ) - { - // Iterate through all the values, and quit if we - // don't find one in the values - for ( String val:vals ) - { - ServerStringValue value = new ServerStringValue( attributeType, val ); - - if ( !values.contains( value ) ) - { - return false; - } - } - - return true; - } - else - { - return false; - } - } - - - /** - *

- * Indicates whether the specified values are some of the attribute's values. - *

- *

- * If the Attribute is HR, te metho will only accept String Values. Otherwise, - * it will only accept Binary values. - *

- * - * @param vals the values - * @return true if this attribute contains all the values, otherwise false - */ - public boolean contains( Value... vals ) - { - // Iterate through all the values, and quit if we - // don't find one in the values. We have to separate the check - // depending on the isHR flag value. - if ( isHR ) - { - for ( Value val:vals ) - { - if ( val instanceof ServerStringValue ) - { - if ( !values.contains( val ) ) - { - return false; - } - } - else if ( val instanceof ClientStringValue ) - { - ServerStringValue serverValue = new ServerStringValue( attributeType, val.isNull() ? (String)null : val.getString() ); - - if ( !values.contains( serverValue ) ) - { - return false; - } - } - else - { - // Not a String value - return false; - } - } - } - else - { - for ( Value val:vals ) - { - if ( val instanceof ClientBinaryValue ) - { - if ( !values.contains( val ) ) - { - return false; - } - } - else - { - // Not a Binary value - return false; - } - } - } - - return true; - } - - - /** - * Get the attribute type associated with this ServerAttribute. - * - * @return the attributeType associated with this entry attribute - */ - public AttributeType getAttributeType() - { - return attributeType; - } - - - /** - *

- * Check if the current attribute type is of the expected attributeType - *

- *

- * This method won't tell if the current attribute is a descendant of - * the attributeType. For instance, the "CN" serverAttribute will return - * false if we ask if it's an instance of "Name". - *

- * - * @param attributeId The AttributeType ID to check - * @return True if the current attribute is of the expected attributeType - * @throws InvalidAttributeValueException If there is no AttributeType - */ - public boolean instanceOf( String attributeId ) throws InvalidAttributeValueException - { - String trimmedId = StringTools.trim( attributeId ); - - if ( StringTools.isEmpty( trimmedId ) ) - { - return false; - } - - String normId = StringTools.lowerCaseAscii( trimmedId ); - - for ( String name:attributeType.getNamesRef() ) - { - if ( normId.equalsIgnoreCase( name ) ) - { - return true; - } - } - - return normId.equalsIgnoreCase( attributeType.getOid() ); - } - - - /** - *

- * Checks to see if this attribute is valid along with the values it contains. - *

- *

- * An attribute is valid if : - *

  • All of its values are valid with respect to the attributeType's syntax checker
  • - *
  • If the attributeType is SINGLE-VALUE, then no more than a value should be present
  • - *

    - * @return true if the attribute and it's values are valid, false otherwise - * @throws NamingException if there is a failure to check syntaxes of values - */ - public boolean isValid() throws NamingException - { - // First check if the attribute has more than one value - // if the attribute is supposed to be SINGLE_VALUE - if ( attributeType.isSingleValue() && ( values.size() > 1 ) ) - { - return false; - } - - for ( Value value : values ) - { - if ( ! value.isValid() ) - { - return false; - } - } - - return true; - } - - - /** - * @see EntryAttribute#remove(byte[]...) - * - * @return true if all the values shave been removed from this attribute - */ - public boolean remove( byte[]... vals ) - { - if ( isHR ) - { - return false; - } - - boolean removed = true; - - for ( byte[] val:vals ) - { - ServerBinaryValue value = new ServerBinaryValue( attributeType, val ); - removed &= values.remove( value ); - } - - return removed; - } - - - /** - * @see EntryAttribute#remove(String...) - * - * @return true if all the values shave been removed from this attribute - */ - public boolean remove( String... vals ) - { - if ( !isHR ) - { - return false; - } - - boolean removed = true; - - for ( String val:vals ) - { - ServerStringValue value = new ServerStringValue( attributeType, val ); - removed &= values.remove( value ); - } - - return removed; - } - - - /** - * @see EntryAttribute#remove(org.apache.directory.shared.ldap.entry.Value...) - * - * @return true if all the values shave been removed from this attribute - */ - public boolean remove( Value... vals ) - { - boolean removed = true; - - // Loop through all the values to remove. If one of - // them is not present, the method will return false. - // As the attribute may be HR or not, we have two separated treatments - if ( isHR ) - { - for ( Value val:vals ) - { - if ( val instanceof ClientStringValue ) - { - ServerStringValue ssv = new ServerStringValue( attributeType, (String)val.get() ); - removed &= values.remove( ssv ); - } - else if ( val instanceof ServerStringValue ) - { - removed &= values.remove( val ); - } - else - { - removed = false; - } - } - } - else - { - for ( Value val:vals ) - { - if ( val instanceof ClientBinaryValue ) - { - ServerBinaryValue sbv = new ServerBinaryValue( attributeType, (byte[])val.get() ); - removed &= values.remove( sbv ); - } - else if ( val instanceof ServerBinaryValue ) - { - removed &= values.remove( val ); - } - else - { - removed = false; - } - } - } - - return removed; - } - - - - /** - *

    - * Set the attribute type associated with this ServerAttribute. - *

    - *

    - * The current attributeType will be replaced. It is the responsibility of - * the caller to insure that the existing values are compatible with the new - * AttributeType - *

    - * - * @param attributeType the attributeType associated with this entry attribute - */ - public void setAttributeType( AttributeType attributeType ) - { - if ( attributeType == null ) - { - throw new IllegalArgumentException( "The AttributeType parameter should not be null" ); - } - - this.attributeType = attributeType; - setUpId( null, attributeType ); - - try - { - if ( attributeType.getSyntax().isHumanReadable() ) - { - isHR = true; - } - else - { - isHR = false; - } - } - catch ( NamingException ne ) - { - // If we have an exception while trying to get the Syntax for this attribute - // just set it as Binary - isHR = false; - } - } - - - /** - *

    - * Overload the ClientAttribte isHR method : we can't change this flag - * for a ServerAttribute, as the HR is already set using the AttributeType. - * Set the attribute to Human Readable or to Binary. - *

    - * - * @param isHR true for a Human Readable attribute, - * false for a Binary attribute. - */ - public void setHR( boolean isHR ) - { - // Do nothing... - } - - - /** - *

    - * Overload the {@link DefaultClientAttribute#setId(String)} method. - *

    - *

    - * As the attributeType has already been set, we have to be sure that the - * argument is compatible with the attributeType's name. - *

    - *

    - * If the given ID is not compatible with the attributeType's possible - * names, the previously loaded ID will be kept. - *

    - * - * @param id The attribute ID - */ - public void setId( String id ) - { - if ( !StringTools.isEmpty( StringTools.trim( id ) ) ) - { - if ( attributeType.getName() == null ) - { - // If the name is null, then we may have to store an OID - if ( OID.isOID( id ) && attributeType.getOid().equals( id ) ) - { - // Everything is fine, store the upId. - // This should not happen... - super.setId( id ); - } - } - else - { - // We have at least one name. Check that the normalized upId - // is one of those names. Otherwise, the upId may be an OID too. - // In this case, it must be equals to the attributeType OID. - String normId = StringTools.lowerCaseAscii( StringTools.trim( id ) ); - - for ( String atName:attributeType.getNamesRef() ) - { - if ( atName.equalsIgnoreCase( normId ) ) - { - // Found ! We can store the upId and get out - super.setId( normId ); - return; - } - } - - // Last case, the UpId is an OID - if ( OID.isOID( normId ) && attributeType.getOid().equals( normId ) ) - { - // We have an OID : stores it - super.setUpId( normId ); - } - else - { - // The id is incorrect : this is not allowed - throw new IllegalArgumentException( "The ID '" + id + "'is incompatible with the AttributeType's id '" + - attributeType.getName() + "'" ); - } - } - } - else - { - throw new IllegalArgumentException( "An ID cannnot be null, empty, or resolved to an emtpy" + - " value when trimmed" ); - } - } - - - /** - *

    - * Overload the {@link DefaultClientAttribute#setUpId(String)} method. - *

    - *

    - * As the attributeType has already been set, we have to be sure that the - * argument is compatible with the attributeType's name. - *

    - *

    - * If the given ID is not compatible with the attributeType's possible - * names, the previously loaded ID will be kept. - *

    - * - * @param upId The attribute ID - */ - public void setUpId( String upId ) - { - if ( !StringTools.isEmpty( StringTools.trim( upId ) ) ) - { - if ( attributeType.getName() == null ) - { - // If the name is null, then we may have to store an OID - if ( OID.isOID( upId ) && attributeType.getOid().equals( upId ) ) - { - // Everything is fine, store the upId. - // This should not happen... - super.setUpId( upId ); - - } - } - else - { - // We have at least one name. Check that the normalized upId - // is one of those names. Otherwise, the upId may be an OID too. - // In this case, it must be equals to the attributeType OID. - String normUpId = StringTools.lowerCaseAscii( StringTools.trim( upId ) ); - - for ( String atId:attributeType.getNamesRef() ) - { - if ( atId.equalsIgnoreCase( normUpId ) ) - { - // Found ! We can store the upId and get out - super.setUpId( upId ); - return; - } - } - - // Last case, the UpId is an OID - if ( OID.isOID( normUpId ) && attributeType.getOid().equals( normUpId ) ) - { - // We have an OID : stores it - super.setUpId( upId ); - } - } - } - } - - - /** - *

    - * Set the user provided ID. If we have none, the upId is assigned - * the attributetype's name. If it does not have any name, we will - * use the OID. - *

    - *

    - * If we have an upId and an AttributeType, they must be compatible. : - * - if the upId is an OID, it must be the AttributeType's OID - * - otherwise, its normalized form must be equals to ones of - * the attributeType's names. - *

    - *

    - * In any case, the ATtributeType will be changed. The caller is responsible for - * the present values to be compatoble with the new AttributeType. - *

    - * - * @param upId The attribute ID - * @param attributeType The associated attributeType - */ - public void setUpId( String upId, AttributeType attributeType ) - { - if ( StringTools.isEmpty( StringTools.trim( upId ) ) ) - { - super.setUpId( getUpId( attributeType ) ); - this.attributeType = attributeType; - } - else - { - String name = attributeType.getName(); - - if ( name == null ) - { - // If the name is null, then we may have to store an OID - if ( OID.isOID( upId ) && attributeType.getOid().equals( upId ) ) - { - // Everything is fine, store the upId. - super.setUpId( upId ); - this.attributeType = attributeType; - } - else - { - // We have a difference or the upId is not a valid OID : - // we will use the attributeTypeOID in this case. - LOG.warn( "The upID ({}) is not an OID or is different from the AttributeType OID({})", - upId, attributeType.getOid() ); - super.setUpId( attributeType.getOid() ); - this.attributeType = attributeType; - } - } - else - { - // We have at least one name. Check that the normalized upId - // is one of those names. Otherwise, the upId may be an OID too. - // In this case, it must be equals to the attributeType OID. - String normUpId = StringTools.lowerCaseAscii( StringTools.trim( upId ) ); - - for ( String atId:attributeType.getNamesRef() ) - { - if ( atId.equalsIgnoreCase( normUpId ) ) - { - // Found ! We can store the upId and get out - super.setUpId( upId ); - this.attributeType = attributeType; - return; - } - } - - // UpId was not found in names. It should be an OID, or if not, we - // will use the AttributeType name. - if ( OID.isOID( normUpId ) && attributeType.getOid().equals( normUpId ) ) - { - // We have an OID : stores it - super.setUpId( upId ); - this.attributeType = attributeType; - } - else - { - String message = "The upID (" + upId + ") is not an OID or is different from the AttributeType OID (" + - attributeType.getOid() + ")"; - // Not a valid OID : use the AttributeTypes OID name instead - LOG.error( message ); - throw new IllegalArgumentException( message ); - } - } - } - } - - - /** - * Convert the ServerAttribute to a ClientAttribute - * - * @return An instance of ClientAttribute - */ - public EntryAttribute toClientAttribute() - { - // Create the new EntryAttribute - EntryAttribute clientAttribute = new DefaultClientAttribute( upId ); - - // Copy the values - for ( Value value:this ) - { - Value clientValue = null; - - if ( value instanceof ServerStringValue ) - { - clientValue = new ClientStringValue( value.getString() ); - } - else - { - clientValue = new ClientBinaryValue( value.getBytes() ); - } - - clientAttribute.add( clientValue ); - } - - return clientAttribute; - } - - - //------------------------------------------------------------------------- - // Serialization methods - //------------------------------------------------------------------------- - - /** - * @see java.io.Externalizable#writeExternal(ObjectOutput) - * - * We can't use this method for a ServerAttribute, as we have to feed the value - * with an AttributeType object - */ - public void writeExternal( ObjectOutput out ) throws IOException - { - throw new IllegalStateException( "Cannot use standard serialization for a ServerAttribute" ); - } - - - /** - * @see Externalizable#writeExternal(ObjectOutput) - *

    - * - * This is the place where we serialize attributes, and all theirs - * elements. - * - * The inner structure is the same as the client attribute, but we can't call - * it as we won't be able to serialize the serverValues - * - */ - public void serialize( ObjectOutput out ) throws IOException - { - // Write the UPId (the id will be deduced from the upID) - out.writeUTF( upId ); - - // Write the HR flag, if not null - if ( isHR != null ) - { - out.writeBoolean( true ); - out.writeBoolean( isHR ); - } - else - { - out.writeBoolean( false ); - } - - // Write the number of values - out.writeInt( size() ); - - if ( size() > 0 ) - { - // Write each value - for ( Value value:values ) - { - // Write the value, using the correct method - if ( value instanceof ServerStringValue ) - { - ((ServerStringValue)value).serialize( out ); - } - else - { - ((ServerBinaryValue)value).serialize( out ); - } - } - } - } - - - /** - * @see java.io.Externalizable#readExternal(ObjectInput) - * - * We can't use this method for a ServerAttribute, as we have to feed the value - * with an AttributeType object - */ - public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException - { - throw new IllegalStateException( "Cannot use standard serialization for a ServerAttribute" ); - } - - - /** - * @see Externalizable#readExternal(ObjectInput) - */ - public void deserialize( ObjectInput in ) throws IOException, ClassNotFoundException - { - // Read the ID and the UPId - upId = in.readUTF(); - - // Compute the id - setUpId( upId ); - - // Read the HR flag, if not null - if ( in.readBoolean() ) - { - isHR = in.readBoolean(); - } - - // Read the number of values - int nbValues = in.readInt(); - - if ( nbValues > 0 ) - { - for ( int i = 0; i < nbValues; i++ ) - { - Value value = null; - - if ( isHR ) - { - value = new ServerStringValue( attributeType ); - ((ServerStringValue)value).deserialize( in ); - } - else - { - value = new ServerBinaryValue( attributeType ); - ((ServerBinaryValue)value).deserialize( in ); - } - - try - { - value.normalize(); - } - catch ( NamingException ne ) - { - // Do nothing... - } - - values.add( value ); - } - } - } - - - //------------------------------------------------------------------------- - // Overloaded Object class methods - //------------------------------------------------------------------------- - /** - * Clone an attribute. All the element are duplicated, so a modification on - * the original object won't affect the cloned object, as a modification - * on the cloned object has no impact on the original object - * - * @return a clone of the current attribute - */ - public ServerAttribute clone() - { - // clone the structure by cloner the inherited class - ServerAttribute clone = (ServerAttribute)super.clone(); - - // We are done ! - return clone; - } - - - /** - * @see Object#equals(Object) - * - * @return true if the two objects are equal - */ - public boolean equals( Object obj ) - { - if ( obj == this ) - { - return true; - } - - if ( ! (obj instanceof ServerAttribute ) ) - { - return false; - } - - ServerAttribute other = (ServerAttribute)obj; - - if ( !attributeType.equals( other.getAttributeType() ) ) - { - return false; - } - - if ( values.size() != other.size() ) - { - return false; - } - - for ( Value val:values ) - { - if ( ! other.contains( val ) ) - { - return false; - } - } - - return true; - } - - - /** - * The hashCode is based on the id, the isHR flag and - * on the internal values. - * - * @see Object#hashCode() - * - * @return the instance's hash code - */ - public int hashCode() - { - int h = super.hashCode(); - - if ( attributeType != null ) - { - h = h*17 + attributeType.hashCode(); - } - - return h; - } - - - /** - * @see Object#toString() - * - * @return A String representation of this instance - */ - public String toString() - { - StringBuilder sb = new StringBuilder(); - - if ( ( values != null ) && ( values.size() != 0 ) ) - { - for ( Value value:values ) - { - sb.append( " " ).append( upId ).append( ": " ); - - if ( value.isNull() ) - { - sb.append( "''" ); - } - else - { - sb.append( value ); - } - - sb.append( '\n' ); - } - } - else - { - sb.append( " " ).append( upId ).append( ": (null)\n" ); - } - - return sb.toString(); - } -} +/* + * 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.server.core.entry; + + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +import javax.naming.NamingException; +import javax.naming.directory.InvalidAttributeValueException; + +import org.apache.directory.shared.asn1.primitives.OID; +import org.apache.directory.shared.ldap.entry.EntryAttribute; +import org.apache.directory.shared.ldap.entry.Value; +import org.apache.directory.shared.ldap.entry.client.ClientBinaryValue; +import org.apache.directory.shared.ldap.entry.client.ClientStringValue; +import org.apache.directory.shared.ldap.entry.client.DefaultClientAttribute; +import org.apache.directory.shared.ldap.schema.AttributeType; +import org.apache.directory.shared.ldap.util.StringTools; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * A server side entry attribute aware of schema. + * + * @author Apache Directory Project + * @version $Rev$, $Date$ + */ +public final class DefaultServerAttribute extends DefaultClientAttribute implements ServerAttribute +{ + public static final long serialVersionUID = 1L; + + /** logger for reporting errors that might not be handled properly upstream */ + private static final Logger LOG = LoggerFactory.getLogger( DefaultServerAttribute.class ); + + /** The associated AttributeType */ + private AttributeType attributeType; + + + //----------------------------------------------------------------------- + // utility methods + //----------------------------------------------------------------------- + /** + * Private helper method used to set an UpId from an attributeType + * + * @param at The attributeType for which we want the upID + * @return the ID of the given attributeType + */ + private String getUpId( AttributeType at ) + { + String atUpId = at.getName(); + + if ( atUpId == null ) + { + atUpId = at.getOid(); + } + + return atUpId; + } + + + //------------------------------------------------------------------------- + // Constructors + //------------------------------------------------------------------------- + /** + * + * Creates a new instance of DefaultServerAttribute, by copying + * another attribute, which can be a ClientAttribute. If the other + * attribute is a ServerAttribute, it will be copied. + * + * @param attributeType The attribute's type + * @param attribute The attribute to be copied + */ + public DefaultServerAttribute( AttributeType attributeType, EntryAttribute attribute ) + { + // Copy the common values. isHR is only available on a ServerAttribute + this.attributeType = attributeType; + this.id = attribute.getId(); + this.upId = attribute.getUpId(); + + if ( attribute instanceof ServerAttribute ) + { + isHR = attribute.isHR(); + + // Copy all the values + for ( Value value:attribute ) + { + add( value.clone() ); + } + } + else + { + + try + { + isHR = attributeType.getSyntax().isHumanReadable(); + } + catch ( NamingException ne ) + { + // Do nothing : the syntax should always exist ... + } + + + // Copy all the values + for ( Value clientValue:attribute ) + { + Value serverValue = null; + + // We have to convert the value first + if ( clientValue instanceof ClientStringValue ) + { + if ( isHR ) + { + serverValue = new ServerStringValue( attributeType, clientValue.getString() ); + } + else + { + // We have to convert the value to a binary value first + serverValue = new ServerBinaryValue( attributeType, + clientValue.getBytes() ); + } + } + else if ( clientValue instanceof ClientBinaryValue ) + { + if ( isHR ) + { + // We have to convert the value to a String value first + serverValue = new ServerStringValue( attributeType, + clientValue.getString() ); + } + else + { + serverValue = new ServerBinaryValue( attributeType, clientValue.getBytes() ); + } + } + + add( serverValue ); + } + } + } + + + // maybe have some additional convenience constructors which take + // an initial value as a string or a byte[] + /** + * Create a new instance of a EntryAttribute, without ID nor value. + * + * @param attributeType the attributeType for the empty attribute added into the entry + */ + public DefaultServerAttribute( AttributeType attributeType ) + { + if ( attributeType == null ) + { + throw new IllegalArgumentException( "The AttributeType parameter should not be null" ); + } + + setAttributeType( attributeType ); + } + + + /** + * Create a new instance of a EntryAttribute, without value. + * + * @param upId the ID for the added attributeType + * @param attributeType the added AttributeType + */ + public DefaultServerAttribute( String upId, AttributeType attributeType ) + { + if ( attributeType == null ) + { + String message = "The AttributeType parameter should not be null"; + LOG.error( message ); + throw new IllegalArgumentException( message ); + } + + setAttributeType( attributeType ); + setUpId( upId ); + } + + + /** + * Doc me more! + * + * If the value does not correspond to the same attributeType, then it's + * wrapped value is copied into a new Value which uses the specified + * attributeType. + * + * @param attributeType the attribute type according to the schema + * @param vals an initial set of values for this attribute + */ + public DefaultServerAttribute( AttributeType attributeType, Value... vals ) + { + this( null, attributeType, vals ); + } + + + /** + * Doc me more! + * + * If the value does not correspond to the same attributeType, then it's + * wrapped value is copied into a new Value which uses the specified + * attributeType. + * + * Otherwise, the value is stored, but as a reference. It's not a copy. + * + * @param upId the ID of the added attribute + * @param attributeType the attribute type according to the schema + * @param vals an initial set of values for this attribute + */ + public DefaultServerAttribute( String upId, AttributeType attributeType, Value... vals ) + { + if ( attributeType == null ) + { + throw new IllegalArgumentException( "The AttributeType parameter should not be null" ); + } + + setAttributeType( attributeType ); + setUpId( upId, attributeType ); + add( vals ); + } + + + /** + * Create a new instance of a EntryAttribute, without ID but with some values. + * + * @param attributeType The attributeType added on creation + * @param vals The added value for this attribute + */ + public DefaultServerAttribute( AttributeType attributeType, String... vals ) + { + this( null, attributeType, vals ); + } + + + /** + * Create a new instance of a EntryAttribute. + * + * @param upId the ID for the added attribute + * @param attributeType The attributeType added on creation + * @param vals the added values for this attribute + */ + public DefaultServerAttribute( String upId, AttributeType attributeType, String... vals ) + { + if ( attributeType == null ) + { + throw new IllegalArgumentException( "The AttributeType parameter should not be null" ); + } + + setAttributeType( attributeType ); + add( vals ); + setUpId( upId, attributeType ); + } + + + /** + * Create a new instance of a EntryAttribute, with some byte[] values. + * + * @param attributeType The attributeType added on creation + * @param vals The value for the added attribute + */ + public DefaultServerAttribute( AttributeType attributeType, byte[]... vals ) + { + this( null, attributeType, vals ); + } + + + /** + * Create a new instance of a EntryAttribute, with some byte[] values. + * + * @param upId the ID for the added attribute + * @param attributeType the AttributeType to be added + * @param vals the values for the added attribute + */ + public DefaultServerAttribute( String upId, AttributeType attributeType, byte[]... vals ) + { + if ( attributeType == null ) + { + throw new IllegalArgumentException( "The AttributeType parameter should not be null" ); + } + + setAttributeType( attributeType ); + add( vals ); + setUpId( upId, attributeType ); + } + + + //------------------------------------------------------------------------- + // API + //------------------------------------------------------------------------- + /** + *

    + * Adds some values to this attribute. If the new values are already present in + * the attribute values, the method has no effect. + *

    + *

    + * The new values are added at the end of list of values. + *

    + *

    + * This method returns the number of values that were added. + *

    + *

    + * If the value's type is different from the attribute's type, + * the value is not added. + *

    + * It's the responsibility of the caller to check if the stored + * values are consistent with the attribute's type. + *

    + * + * @param vals some new values to be added which may be null + * @return the number of added values, or 0 if none has been added + */ + public int add( byte[]... vals ) + { + if ( !isHR ) + { + int nbAdded = 0; + + for ( byte[] val:vals ) + { + Value value = new ServerBinaryValue( attributeType, val ); + + try + { + value.normalize(); + } + catch( NamingException ne ) + { + // The value can't be normalized : we don't add it. + LOG.error( "The value '" + val + "' can't be normalized, it hasn't been added" ); + return 0; + } + + if ( add( value ) != 0 ) + { + nbAdded++; + } + else + { + LOG.error( "The value '" + val + "' is incorrect, it hasn't been added" ); + } + } + + return nbAdded; + } + else + { + // We can't add Binary values into a String serverAttribute + return 0; + } + } + + + /** + *

    + * Adds some values to this attribute. If the new values are already present in + * the attribute values, the method has no effect. + *

    + *

    + * The new values are added at the end of list of values. + *

    + *

    + * This method returns the number of values that were added. + *

    + * If the value's type is different from the attribute's type, + * the value is not added. + * + * @param vals some new values to be added which may be null + * @return the number of added values, or 0 if none has been added + */ + public int add( String... vals ) + { + if ( isHR ) + { + int nbAdded = 0; + + for ( String val:vals ) + { + Value newValue = new ServerStringValue( attributeType, val ); + + if ( !contains( newValue ) ) + { + if ( add( newValue ) != 0 ) + { + nbAdded++; + } + else + { + LOG.error( "The value '" + val + "' is incorrect, it hasn't been added" ); + } + } + } + + return nbAdded; + } + else + { + // We can't add String values into a Binary serverAttribute + return 0; + } + } + + + /** + * @see EntryAttribute#add(org.apache.directory.shared.ldap.entry.Value...) + * + * @return the number of added values into this attribute + */ + public int add( Value... vals ) + { + int nbAdded = 0; + + for ( Value val:vals ) + { + try + { + if ( attributeType.getSyntax().isHumanReadable() ) + { + if ( ( val == null ) || val.isNull() ) + { + Value nullSV = new ServerStringValue( attributeType, (String)null ); + + if ( !values.contains( nullSV ) ) + { + values.add( nullSV ); + nbAdded++; + } + } + else if ( val instanceof ServerStringValue ) + { + if ( !values.contains( val ) ) + { + if ( values.add( val ) ) + { + nbAdded++; + } + } + } + else if ( val instanceof ClientStringValue ) + { + // If we get a Client value, convert it to a Server value first + Value serverStringValue = new ServerStringValue( attributeType, val.getString() ); + + if ( !values.contains( serverStringValue ) ) + { + if ( values.add( serverStringValue ) ) + { + nbAdded++; + } + } + } + else + { + String message = "The value must be a String, as its AttributeType is H/R"; + LOG.error( message ); + } + } + else + { + if ( val == null ) + { + Value nullSV = new ServerBinaryValue( attributeType, (byte[])null ); + + if ( !values.contains( nullSV ) ) + { + values.add( nullSV ); + nbAdded++; + } + } + else if ( ( val instanceof ClientBinaryValue ) ) + { + Value serverBinaryValue = new ServerBinaryValue( attributeType, val.getBytes() ); + + if ( !values.contains( serverBinaryValue ) ) + { + if ( values.add( serverBinaryValue ) ) + { + nbAdded++; + } + } + } + else if ( val instanceof ServerBinaryValue ) + { + if ( !values.contains( val ) ) + { + if ( values.add( val ) ) + { + nbAdded++; + } + } + } + else + { + String message = "The value must be a byte[], as its AttributeType is not H/R"; + LOG.error( message ); + } + } + } + catch ( NamingException ne ) + { + String message = "Error while adding value '" + val.toString() +"' : " + ne.getMessage(); + LOG.error( message ); + } + } + + return nbAdded; + } + + + /** + * Remove all the values from this attribute type, including a + * null value. + */ + public void clear() + { + values.clear(); + } + + + /** + *

    + * Indicates whether all the specified values are attribute's values. If + * at least one value is not an attribute's value, this method will return + * false + *

    + *

    + * If the Attribute is HR, this method will returns false + *

    + * + * @param vals the values + * @return true if this attribute contains all the values, otherwise false + */ + public boolean contains( byte[]... vals ) + { + if ( !isHR ) + { + // Iterate through all the values, and quit if we + // don't find one in the values + for ( byte[] val:vals ) + { + ServerBinaryValue value = new ServerBinaryValue( attributeType, val ); + + try + { + value.normalize(); + } + catch ( NamingException ne ) + { + return false; + } + + if ( !values.contains( value ) ) + { + return false; + } + } + + return true; + } + else + { + return false; + } + } + + + /** + *

    + * Indicates whether all the specified values are attribute's values. If + * at least one value is not an attribute's value, this method will return + * false + *

    + *

    + * If the Attribute is not HR, this method will returns false + *

    + * + * @param vals the values + * @return true if this attribute contains all the values, otherwise false + */ + public boolean contains( String... vals ) + { + if ( isHR ) + { + // Iterate through all the values, and quit if we + // don't find one in the values + for ( String val:vals ) + { + ServerStringValue value = new ServerStringValue( attributeType, val ); + + if ( !values.contains( value ) ) + { + return false; + } + } + + return true; + } + else + { + return false; + } + } + + + /** + *

    + * Indicates whether the specified values are some of the attribute's values. + *

    + *

    + * If the Attribute is HR, te metho will only accept String Values. Otherwise, + * it will only accept Binary values. + *

    + * + * @param vals the values + * @return true if this attribute contains all the values, otherwise false + */ + public boolean contains( Value... vals ) + { + // Iterate through all the values, and quit if we + // don't find one in the values. We have to separate the check + // depending on the isHR flag value. + if ( isHR ) + { + for ( Value val:vals ) + { + if ( val instanceof ServerStringValue ) + { + if ( !values.contains( val ) ) + { + return false; + } + } + else if ( val instanceof ClientStringValue ) + { + ServerStringValue serverValue = new ServerStringValue( attributeType, val.isNull() ? (String)null : val.getString() ); + + if ( !values.contains( serverValue ) ) + { + return false; + } + } + else + { + // Not a String value + return false; + } + } + } + else + { + for ( Value val:vals ) + { + if ( val instanceof ClientBinaryValue ) + { + if ( !values.contains( val ) ) + { + return false; + } + } + else + { + // Not a Binary value + return false; + } + } + } + + return true; + } + + + /** + * Get the attribute type associated with this ServerAttribute. + * + * @return the attributeType associated with this entry attribute + */ + public AttributeType getAttributeType() + { + return attributeType; + } + + + /** + *

    + * Check if the current attribute type is of the expected attributeType + *

    + *

    + * This method won't tell if the current attribute is a descendant of + * the attributeType. For instance, the "CN" serverAttribute will return + * false if we ask if it's an instance of "Name". + *

    + * + * @param attributeId The AttributeType ID to check + * @return True if the current attribute is of the expected attributeType + * @throws InvalidAttributeValueException If there is no AttributeType + */ + public boolean instanceOf( String attributeId ) throws InvalidAttributeValueException + { + String trimmedId = StringTools.trim( attributeId ); + + if ( StringTools.isEmpty( trimmedId ) ) + { + return false; + } + + String normId = StringTools.lowerCaseAscii( trimmedId ); + + for ( String name:attributeType.getNamesRef() ) + { + if ( normId.equalsIgnoreCase( name ) ) + { + return true; + } + } + + return normId.equalsIgnoreCase( attributeType.getOid() ); + } + + + /** + *

    + * Checks to see if this attribute is valid along with the values it contains. + *

    + *

    + * An attribute is valid if : + *

  • All of its values are valid with respect to the attributeType's syntax checker
  • + *
  • If the attributeType is SINGLE-VALUE, then no more than a value should be present
  • + *

    + * @return true if the attribute and it's values are valid, false otherwise + * @throws NamingException if there is a failure to check syntaxes of values + */ + public boolean isValid() throws NamingException + { + // First check if the attribute has more than one value + // if the attribute is supposed to be SINGLE_VALUE + if ( attributeType.isSingleValue() && ( values.size() > 1 ) ) + { + return false; + } + + for ( Value value : values ) + { + if ( ! value.isValid() ) + { + return false; + } + } + + return true; + } + + + /** + * @see EntryAttribute#remove(byte[]...) + * + * @return true if all the values shave been removed from this attribute + */ + public boolean remove( byte[]... vals ) + { + if ( isHR ) + { + return false; + } + + boolean removed = true; + + for ( byte[] val:vals ) + { + ServerBinaryValue value = new ServerBinaryValue( attributeType, val ); + removed &= values.remove( value ); + } + + return removed; + } + + + /** + * @see EntryAttribute#remove(String...) + * + * @return true if all the values shave been removed from this attribute + */ + public boolean remove( String... vals ) + { + if ( !isHR ) + { + return false; + } + + boolean removed = true; + + for ( String val:vals ) + { + ServerStringValue value = new ServerStringValue( attributeType, val ); + removed &= values.remove( value ); + } + + return removed; + } + + + /** + * @see EntryAttribute#remove(org.apache.directory.shared.ldap.entry.Value...) + * + * @return true if all the values shave been removed from this attribute + */ + public boolean remove( Value... vals ) + { + boolean removed = true; + + // Loop through all the values to remove. If one of + // them is not present, the method will return false. + // As the attribute may be HR or not, we have two separated treatments + if ( isHR ) + { + for ( Value val:vals ) + { + if ( val instanceof ClientStringValue ) + { + ServerStringValue ssv = new ServerStringValue( attributeType, (String)val.get() ); + removed &= values.remove( ssv ); + } + else if ( val instanceof ServerStringValue ) + { + removed &= values.remove( val ); + } + else + { + removed = false; + } + } + } + else + { + for ( Value val:vals ) + { + if ( val instanceof ClientBinaryValue ) + { + ServerBinaryValue sbv = new ServerBinaryValue( attributeType, (byte[])val.get() ); + removed &= values.remove( sbv ); + } + else if ( val instanceof ServerBinaryValue ) + { + removed &= values.remove( val ); + } + else + { + removed = false; + } + } + } + + return removed; + } + + + + /** + *

    + * Set the attribute type associated with this ServerAttribute. + *

    + *

    + * The current attributeType will be replaced. It is the responsibility of + * the caller to insure that the existing values are compatible with the new + * AttributeType + *

    + * + * @param attributeType the attributeType associated with this entry attribute + */ + public void setAttributeType( AttributeType attributeType ) + { + if ( attributeType == null ) + { + throw new IllegalArgumentException( "The AttributeType parameter should not be null" ); + } + + this.attributeType = attributeType; + setUpId( null, attributeType ); + + try + { + if ( attributeType.getSyntax().isHumanReadable() ) + { + isHR = true; + } + else + { + isHR = false; + } + } + catch ( NamingException ne ) + { + // If we have an exception while trying to get the Syntax for this attribute + // just set it as Binary + isHR = false; + } + } + + + /** + *

    + * Overload the ClientAttribte isHR method : we can't change this flag + * for a ServerAttribute, as the HR is already set using the AttributeType. + * Set the attribute to Human Readable or to Binary. + *

    + * + * @param isHR true for a Human Readable attribute, + * false for a Binary attribute. + */ + public void setHR( boolean isHR ) + { + // Do nothing... + } + + + /** + *

    + * Overload the {@link DefaultClientAttribute#setId(String)} method. + *

    + *

    + * As the attributeType has already been set, we have to be sure that the + * argument is compatible with the attributeType's name. + *

    + *

    + * If the given ID is not compatible with the attributeType's possible + * names, the previously loaded ID will be kept. + *

    + * + * @param id The attribute ID + */ + public void setId( String id ) + { + if ( !StringTools.isEmpty( StringTools.trim( id ) ) ) + { + if ( attributeType.getName() == null ) + { + // If the name is null, then we may have to store an OID + if ( OID.isOID( id ) && attributeType.getOid().equals( id ) ) + { + // Everything is fine, store the upId. + // This should not happen... + super.setId( id ); + } + } + else + { + // We have at least one name. Check that the normalized upId + // is one of those names. Otherwise, the upId may be an OID too. + // In this case, it must be equals to the attributeType OID. + String normId = StringTools.lowerCaseAscii( StringTools.trim( id ) ); + + for ( String atName:attributeType.getNamesRef() ) + { + if ( atName.equalsIgnoreCase( normId ) ) + { + // Found ! We can store the upId and get out + super.setId( normId ); + return; + } + } + + // Last case, the UpId is an OID + if ( OID.isOID( normId ) && attributeType.getOid().equals( normId ) ) + { + // We have an OID : stores it + super.setUpId( normId ); + } + else + { + // The id is incorrect : this is not allowed + throw new IllegalArgumentException( "The ID '" + id + "'is incompatible with the AttributeType's id '" + + attributeType.getName() + "'" ); + } + } + } + else + { + throw new IllegalArgumentException( "An ID cannnot be null, empty, or resolved to an emtpy" + + " value when trimmed" ); + } + } + + + /** + *

    + * Overload the {@link DefaultClientAttribute#setUpId(String)} method. + *

    + *

    + * As the attributeType has already been set, we have to be sure that the + * argument is compatible with the attributeType's name. + *

    + *

    + * If the given ID is not compatible with the attributeType's possible + * names, the previously loaded ID will be kept. + *

    + * + * @param upId The attribute ID + */ + public void setUpId( String upId ) + { + if ( !StringTools.isEmpty( StringTools.trim( upId ) ) ) + { + if ( attributeType.getName() == null ) + { + // If the name is null, then we may have to store an OID + if ( OID.isOID( upId ) && attributeType.getOid().equals( upId ) ) + { + // Everything is fine, store the upId. + // This should not happen... + super.setUpId( upId ); + + } + } + else + { + // We have at least one name. Check that the normalized upId + // is one of those names. Otherwise, the upId may be an OID too. + // In this case, it must be equals to the attributeType OID. + String normUpId = StringTools.lowerCaseAscii( StringTools.trim( upId ) ); + + for ( String atId:attributeType.getNamesRef() ) + { + if ( atId.equalsIgnoreCase( normUpId ) ) + { + // Found ! We can store the upId and get out + super.setUpId( upId ); + return; + } + } + + // Last case, the UpId is an OID + if ( OID.isOID( normUpId ) && attributeType.getOid().equals( normUpId ) ) + { + // We have an OID : stores it + super.setUpId( upId ); + } + } + } + } + + + /** + *

    + * Set the user provided ID. If we have none, the upId is assigned + * the attributetype's name. If it does not have any name, we will + * use the OID. + *

    + *

    + * If we have an upId and an AttributeType, they must be compatible. : + * - if the upId is an OID, it must be the AttributeType's OID + * - otherwise, its normalized form must be equals to ones of + * the attributeType's names. + *

    + *

    + * In any case, the ATtributeType will be changed. The caller is responsible for + * the present values to be compatoble with the new AttributeType. + *

    + * + * @param upId The attribute ID + * @param attributeType The associated attributeType + */ + public void setUpId( String upId, AttributeType attributeType ) + { + if ( StringTools.isEmpty( StringTools.trim( upId ) ) ) + { + super.setUpId( getUpId( attributeType ) ); + this.attributeType = attributeType; + } + else + { + String name = attributeType.getName(); + + if ( name == null ) + { + // If the name is null, then we may have to store an OID + if ( OID.isOID( upId ) && attributeType.getOid().equals( upId ) ) + { + // Everything is fine, store the upId. + super.setUpId( upId ); + this.attributeType = attributeType; + } + else + { + // We have a difference or the upId is not a valid OID : + // we will use the attributeTypeOID in this case. + LOG.warn( "The upID ({}) is not an OID or is different from the AttributeType OID({})", + upId, attributeType.getOid() ); + super.setUpId( attributeType.getOid() ); + this.attributeType = attributeType; + } + } + else + { + // We have at least one name. Check that the normalized upId + // is one of those names. Otherwise, the upId may be an OID too. + // In this case, it must be equals to the attributeType OID. + String normUpId = StringTools.lowerCaseAscii( StringTools.trim( upId ) ); + + for ( String atId:attributeType.getNamesRef() ) + { + if ( atId.equalsIgnoreCase( normUpId ) ) + { + // Found ! We can store the upId and get out + super.setUpId( upId ); + this.attributeType = attributeType; + return; + } + } + + // UpId was not found in names. It should be an OID, or if not, we + // will use the AttributeType name. + if ( OID.isOID( normUpId ) && attributeType.getOid().equals( normUpId ) ) + { + // We have an OID : stores it + super.setUpId( upId ); + this.attributeType = attributeType; + } + else + { + String message = "The upID (" + upId + ") is not an OID or is different from the AttributeType OID (" + + attributeType.getOid() + ")"; + // Not a valid OID : use the AttributeTypes OID name instead + LOG.error( message ); + throw new IllegalArgumentException( message ); + } + } + } + } + + + /** + * Convert the ServerAttribute to a ClientAttribute + * + * @return An instance of ClientAttribute + */ + public EntryAttribute toClientAttribute() + { + // Create the new EntryAttribute + EntryAttribute clientAttribute = new DefaultClientAttribute( upId ); + + // Copy the values + for ( Value value:this ) + { + Value clientValue = null; + + if ( value instanceof ServerStringValue ) + { + clientValue = new ClientStringValue( value.getString() ); + } + else + { + clientValue = new ClientBinaryValue( value.getBytes() ); + } + + clientAttribute.add( clientValue ); + } + + return clientAttribute; + } + + + //------------------------------------------------------------------------- + // Serialization methods + //------------------------------------------------------------------------- + + /** + * @see java.io.Externalizable#writeExternal(ObjectOutput) + * + * We can't use this method for a ServerAttribute, as we have to feed the value + * with an AttributeType object + */ + public void writeExternal( ObjectOutput out ) throws IOException + { + throw new IllegalStateException( "Cannot use standard serialization for a ServerAttribute" ); + } + + + /** + * @see Externalizable#writeExternal(ObjectOutput) + *

    + * + * This is the place where we serialize attributes, and all theirs + * elements. + * + * The inner structure is the same as the client attribute, but we can't call + * it as we won't be able to serialize the serverValues + * + */ + public void serialize( ObjectOutput out ) throws IOException + { + // Write the UPId (the id will be deduced from the upID) + out.writeUTF( upId ); + + // Write the HR flag, if not null + if ( isHR != null ) + { + out.writeBoolean( true ); + out.writeBoolean( isHR ); + } + else + { + out.writeBoolean( false ); + } + + // Write the number of values + out.writeInt( size() ); + + if ( size() > 0 ) + { + // Write each value + for ( Value value:values ) + { + // Write the value, using the correct method + if ( value instanceof ServerStringValue ) + { + ((ServerStringValue)value).serialize( out ); + } + else + { + ((ServerBinaryValue)value).serialize( out ); + } + } + } + } + + + /** + * @see java.io.Externalizable#readExternal(ObjectInput) + * + * We can't use this method for a ServerAttribute, as we have to feed the value + * with an AttributeType object + */ + public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException + { + throw new IllegalStateException( "Cannot use standard serialization for a ServerAttribute" ); + } + + + /** + * @see Externalizable#readExternal(ObjectInput) + */ + public void deserialize( ObjectInput in ) throws IOException, ClassNotFoundException + { + // Read the ID and the UPId + upId = in.readUTF(); + + // Compute the id + setUpId( upId ); [... 160 lines stripped ...]