/*
 * 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
 *
 *  https://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.api.ldap.model.entry;


import java.io.Externalizable;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.apache.directory.api.ldap.model.exception.LdapException;
import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.directory.api.ldap.model.schema.AttributeType;


/**
 * This interface represent a LDAP entry. An LDAP entry contains :
 * <ul>
 *   <li> A distinguished name (Dn)</li>
 *   <li> A list of attributes</li>
 * </ul>
 * <p>
 * The available methods on this object are described in this interface.
 * </p>
 * <p>
 * This interface is used by the serverEntry and clientEntry interfaces.
 *</p>
 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
 */
public interface Entry extends Cloneable, Iterable<Attribute>, Externalizable
{
    /**
     * Remove all the attributes for this entry. The Dn is not reset
     */
    void clear();


    /**
     * Clone the current entry
     *
     * @return a full copy of this entry
     */
    Entry clone();


    /**
     * Shallow Clone the current entry. We don't deep clone the attributes
     *
     * @return a shallow copy of this entry
     */
    Entry shallowClone();


    /**
     * Get this entry's Dn.
     *
     * @return The entry's Dn
     */
    Dn getDn();


    /**
     * Tells if an entry as some specific ObjectClasses values
     *
     * @param objectClasses The ObjectClasses we want to check
     * @return <code>true</code> if all the ObjectClasses value are present
     * in the ObjectClass attribute
     */
    boolean hasObjectClass( String... objectClasses );


    /**
     * Tells if an entry has some specific ObjectClasses Attributes
     *
     * @param objectClasses The ObjectClasses we want to check
     * @return <code>true</code> if the ObjectClasses Attribute are present
     * in the ObjectClass attribute
     */
    boolean hasObjectClass( Attribute... objectClasses );


    /**
     * <p>
     * Returns the attribute with the specified alias. The return value
     * is <code>null</code> if no match is found.
     * </p>
     * <p>An Attribute with an id different from the supplied alias may
     * be returned: for example a call with 'cn' may in some implementations
     * return an Attribute whose getId() field returns 'commonName'.
     * </p>
     *
     * @param alias an aliased name of the attribute identifier
     * @return the attribute associated with the alias, <code>null</code> otherwise
     */
    Attribute get( String alias );


    /**
     * Returns the attribute associated with an AttributeType
     *
     * @param attributeType the AttributeType we are looking for
     * @return the associated attribute, <code>null</code> otherwise
     */
    Attribute get( AttributeType attributeType );


    /**
     * Gets all the attributes
     *
     * @return The combined set of all the attributes.
     */
    Collection<Attribute> getAttributes();


    /**
     * Set this entry's Dn.
     *
     * @param dn The Dn associated with this entry
     */
    void setDn( Dn dn );


    /**
     * Set this entry's Dn.
     *
     * @param dn The String Dn associated with this entry
     * @throws LdapInvalidDnException if the provided Dn is invalid
     */
    void setDn( String dn ) throws LdapInvalidDnException;


    /**
     * Returns an iterator on the attributes for this entry.
     *
     * @return an iterator on top of all contained attributes
     */
    @Override
    Iterator<Attribute> iterator();


    /**
     * Add some Attributes to the current Entry.
     *
     * @param attributes The attributes to add
     * @return the modified entry
     * @throws LdapException If we can't add any of the attributes
     */
    Entry add( Attribute... attributes ) throws LdapException;


    /**
     * <p>
     * Add an attribute (represented by its AttributeType and some binary values) into an
     * entry.
     * </p>
     * <p>
     * If we already have an attribute with the same values, the duplicated values
     * are not added (duplicated values are not allowed)
     * </p>
     * <p>
     * If the value cannot be added, or if the AttributeType is null or invalid,
     * a LdapException is thrown.
     * </p>
     *
     * @param attributeType The attribute Type.
     * @param values The list of binary values to inject. It can be empty.
     * @return the modified entry
     * @throws LdapException If the attribute does not exist
     */
    Entry add( AttributeType attributeType, byte[]... values ) throws LdapException;


    /**
     * <p>
     * Add an attribute (represented by its AttributeType and some String values) into an
     * entry.
     * </p>
     * <p>
     * If we already have an attribute with the same values, the duplicated values
     * are not added (duplicated values are not allowed)
     * </p>
     * <p>
     * If the value cannot be added, or if the AttributeType is null or invalid,
     * a LdapException is thrown.
     * </p>
     *
     * @param attributeType The attribute Type
     * @param values The list of binary values to inject. It can be empty
     * @return the modified entry
     * @throws org.apache.directory.api.ldap.model.exception.LdapException If the attribute does not exist
     */
    Entry add( AttributeType attributeType, String... values ) throws LdapException;


    /**
     * <p>
     * Add an attribute (represented by its AttributeType and some values) into an
     * entry.
     * </p>
     * <p>
     * If we already have an attribute with the same values, the duplicated values
     * are not added (duplicated values are not allowed)
     * </p>
     * <p>
     * If the value cannot be added, or if the AttributeType is null or invalid,
     * a LdapException is thrown.
     * </p>
     *
     * @param attributeType The attribute Type
     * @param values The list of binary values to inject. It can be empty
     * @return the modified entry
     * @throws LdapException If the attribute does not exist
     */
    Entry add( AttributeType attributeType, Value... values ) throws LdapException;


    /**
     * <p>
     * Add an attribute (represented by its AttributeType and some binary values) into an
     * entry. Set the User Provider ID at the same time
     * </p>
     * <p>
     * If we already have an attribute with the same values, the duplicated values
     * are not added (duplicated values are not allowed)
     * </p>
     * <p>
     * If the value cannot be added, or if the AttributeType is null or invalid,
     * a LdapException is thrown.
     * </p>
     *
     * @param upId The user provided ID for the added AttributeType
     * @param attributeType The attribute Type.
     * @param values The list of binary values to add. It can be empty.
     * @return the modified entry
     * @throws LdapException If the attribute does not exist
     */
    Entry add( String upId, AttributeType attributeType, byte[]... values ) throws LdapException;


    /**
     * <p>
     * Add an attribute (represented by its AttributeType and some String values) into an
     * entry. Set the User Provider ID at the same time
     * </p>
     * <p>
     * If we already have an attribute with the same values, the duplicated values
     * are not added (duplicated values are not allowed)
     * </p>
     * <p>
     * If the value cannot be added, or if the AttributeType is null or invalid,
     * a LdapException is thrown.
     * </p>
     *
     * @param upId The user provided ID for the added AttributeType
     * @param attributeType The attribute Type.
     * @param values The list of String values to add. It can be empty.
     * @return the modified entry
     * @throws LdapException If the attribute does not exist
     */
    Entry add( String upId, AttributeType attributeType, String... values ) throws LdapException;


    /**
     * <p>
     * Add an attribute (represented by its AttributeType and some values) into an
     * entry. Set the User Provider ID at the same time
     * </p>
     * <p>
     * If we already have an attribute with the same values, nothing is done
     * (duplicated values are not allowed)
     * </p>
     * <p>
     * If the value cannot be added, or if the AttributeType is null or invalid,
     * a LdapException is thrown.
     * </p>
     *
     * @param upId The user provided ID for the added AttributeType
     * @param attributeType The attribute Type.
     * @param values The list of values to add. It can be empty.
     * @return the modified entry
     * @throws LdapException If the attribute does not exist
     */
    Entry add( String upId, AttributeType attributeType, Value... values ) throws LdapException;


    /**
     * Add some String values to the current Entry.
     *
     * @param upId The user provided ID of the attribute we want to add
     * some values to
     * @param values The list of String values to add
     * @return the modified entry
     * @throws LdapException If we can't add any of the values
     */
    Entry add( String upId, String... values ) throws LdapException;


    /**
     * Add some binary values to the current Entry.
     *
     * @param upId The user provided ID of the attribute we want to add
     * some values to
     * @param values The list of binary values to add
     * @return the modified entry
     * @throws LdapException If we can't add any of the values
     */
    Entry add( String upId, byte[]... values ) throws LdapException;


    /**
     * Add some Values to the current Entry.
     *
     * @param upId The user provided ID of the attribute we want to add
     * some values to
     * @param values The list of Values to add
     * @return the modified entry
     * @throws LdapException If we can't add any of the values
     */
    Entry add( String upId, Value... values ) throws LdapException;


    /**
     * <p>
     * Places attributes in the attribute collection.
     * </p>
     * <p>If there is already an attribute with the same ID as any of the
     * new attributes, the old ones are removed from the collection and
     * are returned by this method. If there was no attribute with the
     * same ID the return value is <code>null</code>.
     *</p>
     *
     * @param attributes the attributes to be put
     * @return the old attributes with the same OID, if exist; otherwise <code>null</code>
     * @exception LdapException if the operation fails
     */
    List<Attribute> put( Attribute... attributes ) throws LdapException;


    /**
     * <p>
     * Places a new attribute with the supplied AttributeType and binary values
     * into the attribute collection.
     * </p>
     * <p>
     * If there is already an attribute with the same AttributeType, the old
     * one is removed from the collection and is returned by this method.
     * </p>
     * <p>
     * This method provides a mechanism to put an attribute with a
     * <code>null</code> value: the value may be <code>null</code>.
     *
     * @param attributeType the type of the new attribute to be put
     * @param values the binary values of the new attribute to be put
     * @return the old attribute with the same identifier, if exists; otherwise
     * <code>null</code>
     * @throws org.apache.directory.api.ldap.model.exception.LdapException if there are failures
     */
    Attribute put( AttributeType attributeType, byte[]... values ) throws LdapException;


    /**
     * <p>
     * Places a new attribute with the supplied AttributeType and String values
     * into the attribute collection.
     * </p>
     * <p>
     * If there is already an attribute with the same AttributeType, the old
     * one is removed from the collection and is returned by this method.
     * </p>
     * <p>
     * This method provides a mechanism to put an attribute with a
     * <code>null</code> value: the value may be <code>null</code>.
     *
     * @param attributeType the type of the new attribute to be put
     * @param values the String values of the new attribute to be put
     * @return the old attribute with the same identifier, if exists; otherwise
     * <code>null</code>
     * @throws org.apache.directory.api.ldap.model.exception.LdapException if there are failures
     */
    Attribute put( AttributeType attributeType, String... values ) throws LdapException;


    /**
     * <p>
     * Places a new attribute with the supplied AttributeType and some values
     * into the attribute collection.
     * </p>
     * <p>
     * If there is already an attribute with the same AttributeType, the old
     * one is removed from the collection and is returned by this method.
     * </p>
     * <p>
     * This method provides a mechanism to put an attribute with a
     * <code>null</code> value: the value may be <code>null</code>.
     *
     * @param attributeType the type of the new attribute to be put
     * @param values the values of the new attribute to be put
     * @return the old attribute with the same identifier, if exists; otherwise
     * <code>null</code>
     * @throws LdapException if there are failures
     */
    Attribute put( AttributeType attributeType, Value... values ) throws LdapException;


    /**
     * <p>
     * Places a new attribute with the supplied AttributeType and some binary values
     * into the attribute collection.
     * </p>
     * <p>
     * The given User provided ID will be used for this new AttributeEntry.
     * </p>
     * <p>
     * If there is already an attribute with the same AttributeType, the old
     * one is removed from the collection and is returned by this method.
     * </p>
     * <p>
     * This method provides a mechanism to put an attribute with a
     * <code>null</code> value: the value may be <code>null</code>.
     *
     * @param upId The User Provided ID to be stored into the AttributeEntry
     * @param attributeType The attributeType to use
     * @param values The values to store
     * @return the old attribute with the same identifier, if exists; otherwise
     * <code>null</code>
     * @throws LdapException if there are failures.
     */
    Attribute put( String upId, AttributeType attributeType, byte[]... values ) throws LdapException;


    /**
     * <p>
     * Places a new attribute with the supplied AttributeType and some String values
     * into the attribute collection.
     * </p>
     * <p>
     * The given User provided ID will be used for this new AttributeEntry.
     * </p>
     * <p>
     * If there is already an attribute with the same AttributeType, the old
     * one is removed from the collection and is returned by this method.
     * </p>
     * <p>
     * This method provides a mechanism to put an attribute with a
     * <code>null</code> value: the value may be <code>null</code>.
     *
     * @param upId The User Provided ID to be stored into the AttributeEntry
     * @param attributeType the type of the new attribute to be put
     * @param values the String values of the new attribute to be put
     * @return the old attribute with the same identifier, if exists; otherwise
     * <code>null</code>
     * @throws org.apache.directory.api.ldap.model.exception.LdapException if there are failures.
     */
    Attribute put( String upId, AttributeType attributeType, String... values ) throws LdapException;


    /**
     * <p>
     * Places a new attribute with the supplied AttributeType and some values
     * into the attribute collection.
     * </p>
     * <p>
     * The given User provided ID will be used for this new AttributeEntry.
     * </p>
     * <p>
     * If there is already an attribute with the same AttributeType, the old
     * one is removed from the collection and is returned by this method.
     * </p>
     * <p>
     * This method provides a mechanism to put an attribute with a
     * <code>null</code> value: the value may be <code>null</code>.
     *
     * @param upId The User Provided ID to be stored into the AttributeEntry
     * @param attributeType the type of the new attribute to be put
     * @param values the values of the new attribute to be put
     * @return the old attribute with the same identifier, if exists; otherwise
     * <code>null</code>
     * @throws LdapException if there are failures.
     */
    Attribute put( String upId, AttributeType attributeType, Value... values ) throws LdapException;


    /**
     * <p>
     * Put an attribute (represented by its ID and some binary values) into an entry.
     * </p>
     * <p>
     * If the attribute already exists, the previous attribute will be
     * replaced and returned.
     * </p>
     *
     * @param upId The attribute ID
     * @param values The list of binary values to put. It can be empty.
     * @return The replaced attribute
     */
    Attribute put( String upId, byte[]... values );


    /**
     * <p>
     * Put an attribute (represented by its ID and some String values) into an entry.
     * </p>
     * <p>
     * If the attribute already exists, the previous attribute will be
     * replaced and returned.
     * </p>
     *
     * @param upId The attribute ID
     * @param values The list of String values to put. It can be empty.
     * @return The replaced attribute
     */
    Attribute put( String upId, String... values );


    /**
     * <p>
     * Put an attribute (represented by its ID and some values) into an entry.
     * </p>
     * <p>
     * If the attribute already exists, the previous attribute will be
     * replaced and returned.
     * </p>
     *
     * @param upId The attribute ID
     * @param values The list of values to put. It can be empty.
     * @return The replaced attribute
     */
    Attribute put( String upId, Value... values );


    /**
     * <p>
     * Removes the specified binary values from an attribute.
     * </p>
     * <p>
     * If at least one value is removed, this method returns <code>true</code>.
     * </p>
     * <p>
     * If there is no more value after having removed the values, the attribute
     * will be removed too.
     * </p>
     * <p>
     * If the attribute does not exist, nothing is done and the method returns
     * <code>false</code>
     * </p>
     *
     * @param attributeType The attribute type
     * @param values the values to be removed
     * @return <code>true</code> if at least a value is removed, <code>false</code>
     * if not all the values have been removed or if the attribute does not exist.
     * @throws LdapException If the removal failed
     */
    boolean remove( AttributeType attributeType, byte[]... values ) throws LdapException;


    /**
     * <p>
     * Removes the specified String values from an attribute.
     * </p>
     * <p>
     * If at least one value is removed, this method returns <code>true</code>.
     * </p>
     * <p>
     * If there is no more value after having removed the values, the attribute
     * will be removed too.
     * </p>
     * <p>
     * If the attribute does not exist, nothing is done and the method returns
     * <code>false</code>
     * </p>
     *
     * @param attributeType The attribute type
     * @param values the values to be removed
     * @return <code>true</code> if at least a value is removed, <code>false</code>
     * if not all the values have been removed or if the attribute does not exist.
     * @throws LdapException If the removal failed
     */
    boolean remove( AttributeType attributeType, String... values ) throws LdapException;


    /**
     * <p>
     * Removes the specified values from an attribute.
     * </p>
     * <p>
     * If at least one value is removed, this method returns <code>true</code>.
     * </p>
     * <p>
     * If there is no more value after having removed the values, the attribute
     * will be removed too.
     * </p>
     * <p>
     * If the attribute does not exist, nothing is done and the method returns
     * <code>false</code>
     * </p>
     *
     * @param attributeType The attribute type
     * @param values the values to be removed
     * @return <code>true</code> if at least a value is removed, <code>false</code>
     * if not all the values have been removed or if the attribute does not exist.
     * @throws LdapException If the removal failed
     */
    boolean remove( AttributeType attributeType, Value... values ) throws LdapException;


    /**
     * Removes the specified attributes. The removed attributes are
     * returned by this method. If there were no attribute the return value
     * is <code>null</code>.
     *
     * @param attributes the attributes to be removed
     * @return the removed attribute, if exists; otherwise <code>null</code>
     * @throws LdapException If the removal failed
     */
    List<Attribute> remove( Attribute... attributes ) throws LdapException;


    /**
     * <p>
     * Removes the attribute with the specified AttributeTypes.
     * </p>
     * <p>
     * The removed attribute are returned by this method.
     * </p>
     * <p>
     * If there is no attribute with the specified AttributeTypes,
     * the return value is <code>null</code>.
     * </p>
     *
     * @param attributes the AttributeTypes to be removed
     */
    void removeAttributes( AttributeType... attributes );


    /**
     * <p>
     * Removes the specified binary values from an attribute.
     * </p>
     * <p>
     * If at least one value is removed, this method returns <code>true</code>.
     * </p>
     * <p>
     * If there is no more value after having removed the values, the attribute
     * will be removed too.
     * </p>
     * <p>
     * If the attribute does not exist, nothing is done and the method returns
     * <code>false</code>
     * </p>
     *
     * @param upId The attribute ID
     * @param values the attribute's values to be removed
     * @return <code>true</code> if at least a value is removed, <code>false</code>
     * if not all the values have been removed or if the attribute does not exist.
     * @throws LdapException If the removal failed
     */
    boolean remove( String upId, byte[]... values ) throws LdapException;


    /**
     * <p>
     * Removes the specified String values from an attribute.
     * </p>
     * <p>
     * If at least one value is removed, this method returns <code>true</code>.
     * </p>
     * <p>
     * If there is no more value after havong removed the values, the attribute
     * will be removed too.
     * </p>
     * <p>
     * If the attribute does not exist, nothing is done and the method returns
     * <code>false</code>
     * </p>
     *
     * @param upId The attribute ID
     * @param values the attribute's values to be removed
     * @return <code>true</code> if at least a value is removed, <code>false</code>
     * if no values have been removed or if the attribute does not exist.
     * @throws LdapException If the provided values are invalid
     */
    boolean remove( String upId, String... values ) throws LdapException;


    /**
     * <p>
     * Removes the specified values from an attribute.
     * </p>
     * <p>
     * If at least one value is removed, this method returns <code>true</code>.
     * </p>
     * <p>
     * If there is no more value after having removed the values, the attribute
     * will be removed too.
     * </p>
     * <p>
     * If the attribute does not exist, nothing is done and the method returns
     * <code>false</code>
     * </p>
     *
     * @param upId The attribute ID
     * @param values the attribute's values to be removed
     * @return <code>true</code> if at least a value is removed, <code>false</code>
     * if not all the values have been removed or if the attribute does not exist.
     * @throws LdapException if the attribute does not exists
     */
    boolean remove( String upId, Value... values ) throws LdapException;


    /**
      * <p>
      * Removes the attribute with the specified alias.
      * </p>
      * <p>
      * The removed attribute are returned by this method.
      * </p>
      * <p>
      * If there is no attribute with the specified alias,
      * the return value is <code>null</code>.
      * </p>
      *
      * @param attributes an aliased name of the attribute to be removed
      */
    void removeAttributes( String... attributes );


    // -----------------------------------------------------------------------
    // Container (contains/get/put/remove) Methods
    // -----------------------------------------------------------------------
    /**
     * Checks if an entry contains an attribute with some given binary values.
     *
     * @param attributeType The Attribute we are looking for.
     * @param values The searched binary values.
     * @return <code>true</code> if all the values are found within the attribute,
     * <code>false</code> otherwise, or if the attributes does not exist.
     */
    boolean contains( AttributeType attributeType, byte[]... values );


    /**
     * Checks if an entry contains an attribute with some given String values.
     *
     * @param attributeType The Attribute we are looking for.
     * @param values The searched String values.
     * @return <code>true</code> if all the values are found within the attribute,
     * <code>false</code> otherwise, or if the attributes does not exist.
     */
    boolean contains( AttributeType attributeType, String... values );


    /**
     * Checks if an entry contains an attribute with some given binary values.
     *
     * @param attributeType The Attribute we are looking for.
     * @param values The searched values.
     * @return <code>true</code> if all the values are found within the attribute,
     * <code>false</code> otherwise, or if the attributes does not exist.
     */
    boolean contains( AttributeType attributeType, Value... values );


    /**
     * Checks if an entry contains a specific AttributeType.
     *
     * @param attributeType The AttributeType to look for.
     * @return <code>true</code> if the attribute is found within the entry.
     */
    boolean containsAttribute( AttributeType attributeType );


    /**
     * <p>
     * Checks if an entry contains a list of attributes.
     * </p>
     * <p>
     * If the list is null or empty, this method will return <code>true</code>
     * if the entry has no attribute, <code>false</code> otherwise.
     * </p>
     *
     * @param attributes The Attributes to look for
     * @return <code>true</code> if all the attributes are found within
     * the entry, <code>false</code> if at least one of them is not present.
     */
    boolean contains( Attribute... attributes );


    /**
     * Checks if an entry contains an attribute with some binary values.
     *
     * @param upId The Attribute we are looking for.
     * @param values The searched values.
     * @return <code>true</code> if all the values are found within the attribute,
     * false if at least one value is not present or if the ID is not valid.
     */
    boolean contains( String upId, byte[]... values );


    /**
     * Checks if an entry contains an attribute with some String values.
     *
     * @param upId The Attribute we are looking for.
     * @param values The searched values.
     * @return <code>true</code> if all the values are found within the attribute,
     * false if at least one value is not present or if the ID is not valid.
     */
    boolean contains( String upId, String... values );


    /**
     * Checks if an entry contains an attribute with some values.
     *
     * @param upId The Attribute we are looking for.
     * @param values The searched values.
     * @return <code>true</code> if all the values are found within the attribute,
     * false if at least one value is not present or if the ID is not valid.
     */
    boolean contains( String upId, Value... values );


    /**
     * Checks if an entry contains some specific attributes.
     *
     * @param attributes The Attributes to look for.
     * @return <code>true</code> if the attributes are all found within the entry.
     */
    boolean containsAttribute( String... attributes );


    /**
     * Returns the number of attributes.
     *
     * @return the number of attributes
     */
    int size();


    /**
     * Tells if the Entry is schema aware
     * @return true if the Entry is schema aware
     */
    boolean isSchemaAware();


    /**
     * A pretty-pinter for Entries
     *
     * @param tabs The tabs to add before any output
     * @return The pretty-printed entry
     */
    String toString( String tabs );
}
