/*************************************************************************
 Copyright © 2000-2003 Novell, Inc. All Rights Reserved.

 THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND
 TREATIES. USE AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO THE LICENSE
 AGREEMENT ACCOMPANYING THE SOFTWARE DEVELOPMENT KIT (SDK) THAT CONTAINS
 THIS WORK. PURSUANT TO THE SDK LICENSE AGREEMENT, NOVELL HEREBY GRANTS
 TO DEVELOPER A ROYALTY-FREE, NON-EXCLUSIVE LICENSE TO INCLUDE NOVELL'S
 SAMPLE CODE IN ITS PRODUCT. NOVELL GRANTS DEVELOPER WORLDWIDE DISTRIBUTION
 RIGHTS TO MARKET, DISTRIBUTE, OR SELL NOVELL'S SAMPLE CODE AS A COMPONENT
 OF DEVELOPER'S PRODUCTS. NOVELL SHALL HAVE NO OBLIGATIONS TO DEVELOPER OR
 DEVELOPER'S CUSTOMERS WITH RESPECT TO THIS CODE.
 ************************************************************************/

package com.novell.nds.dirxml.driver.vrtest;


import java.io.*;
import java.util.*;

import com.novell.nds.dirxml.vrtest.*;


/** An object on the VRTest server. */
class VRTestObject
        implements VRTestConstants, Constants
{

    static final private int DN_LENGTH = 20;
    //big enough to handle most DNs


    private long handle;
    private VRTestAPIWrapper api;
    protected String association;
    protected String dn;
    protected String name;
    protected LinkedList otherNames;
    protected String className;
    protected String path;
    protected boolean hierarchical;
    protected VRTestClassSchema classSchema;
    protected VRTestAttributeList attributes;
    protected VRTestAttributeSchema namingAttributeSchema;


    //used by subclass VRTestRootObject
    protected VRTestObject(String aName,
                           String aPath,
                           String aDN,
                           String anAssociation
                           )
    {
        name = aName;
        path = aPath;
        dn = aDN;
        association = anAssociation;
        attributes = VRTestAttributeList.EMPTY;

        className = null;
        classSchema = null;
        handle = VRTestHandle.INVALID;
        api = null;
        hierarchical = true;
        otherNames = null;
        namingAttributeSchema = null;
    }//VRTestObject(String, String, String, String)


    VRTestObject(long aHandle,
                 String aName, //may be null
                 String aClassName,
                 VRTestAPIWrapper anAPI
                 )
            throws VRTestException
    {
        //constructed in package with previously varified data

        api = anAPI;
        className = aClassName;
        handle = aHandle;
        name = aName; //may be null
        attributes = VRTestAttributeList.EMPTY;
        dn = null;
        association = null;
        path = null;
        classSchema = null;
        hierarchical = api.getSchema().isHierarchical();
        otherNames = null;
        namingAttributeSchema = null;
    }//VRTestObject(long, String, String, VRTestAPIWrapper)


    VRTestClassSchema getClassSchema()
            throws VRTestException
    {
        if (classSchema == null)
        {
            classSchema = api.getSchema().getClassSchema(className);
            if (classSchema == null)
            {
                throw new VRTestException
                        (Errors.noClassSchema(className));
            }
        }//if

        return classSchema;
    }//getClassSchema():VRTestClassSchema


    long getHandle()
    {
        return handle;
    } //getHandle():long


    String getName()
            throws VRTestException, IOException
    {
        if (name == null)
        {
            setNames();
        }

        return name;
    }//getName():String


    LinkedList getOtherNames()
            throws VRTestException, IOException
    {
        //if we have the name, there are not other names
        if (name == null)
        {
            setNames();
        }

        return otherNames;
    }//getOtherNames():LinkedList


    List getAttributes()
            throws VRTestException, IOException
    {
        setAttributes();
        return attributes.attributes();
    }//getAttributes():List


    private void setAttributes()
            throws VRTestException, IOException
    {
        //get attributes if we don't already have them
        if (attributes == VRTestAttributeList.EMPTY)
        {
            attributes = api.getAttributes(handle);
        }
    }//setAttributes():void


    private void setNames()
            throws VRTestException, IOException
    {
        LinkedList namingAttribute;
        setAttributes();
        getNamingAttributeSchema();
        namingAttribute = attributes.getAttribute(namingAttributeSchema);

        if (namingAttribute != null)
        {
            if (namingAttributeSchema.isMultiValued())
            {
                ListIterator iterator;

                name = ((VRTestAttribute) namingAttribute.getFirst()).getValue();
                namingAttribute.removeFirst();
                iterator = namingAttribute.listIterator();
                while (iterator.hasNext())
                {
                    if (otherNames == null)
                    {
                        otherNames = new LinkedList();
                    }
                    otherNames.add(((VRTestAttribute) iterator.next()).getValue());
                }
            }//if
            else  //single-valued
            {
                name = ((VRTestAttribute) namingAttribute.getFirst()).getValue();
            }
        }//if
    }//setNames():void


    //package
    String
            extractAssociationText()
            throws
            VRTestException,
            IOException
    {
        //get the association value if we don't already have it
        if (this.association == null)
        {
            VRTestAttribute attribute = getAttributeByName(UNIQUE_ID);
            if (attribute != null)
            {
                this.association = attribute.getValue();
            }
        }//if

        return this.association;
    } //extractAssociationText()


    //package
    VRTestAttribute
            getAttributeByName(String attributeName)
            throws VRTestException,
            IOException
    {
        setAttributes();
        return this.attributes.getAttributeByName(attributeName);
    } //getAttributeByName()


    //package
    String
            getClassName()
    {
        return className;
    }


    String getDN()
            throws VRTestException, IOException
    {
        if (dn == null)
        {
            setDN();
        }

        return dn;
    }//getDN():String


    private void setDN()
            throws VRTestException, IOException
    {
        StringBuffer dnBuffer;

        dnBuffer = new StringBuffer(DN_LENGTH);
        if (hierarchical)
        {
            getPath();
            if (path == null)
            {
                //can't construct srcDN without a path
                return;
            }
            dnBuffer.append(path);
        }

        getName();
        if (name == null)
        {
            //can't construct srcDN without a name
            return;
        }

        dnBuffer.append(name);
        dn = dnBuffer.toString();
    }//setDN():void


    String getPath()
            throws VRTestException, IOException
    {
        //objects in a flat namespace don't have a path
        if ((path == null) &&
                hierarchical)
        {
            setPath();
        }

        return path;
    } //getPath():String


    private void setPath()
            throws VRTestException, IOException
    {
        VRTestAttribute attribute;

        setAttributes();
        attribute = attributes.getPath();
        if (attribute != null)
        {
            path = attribute.getValue();
        }
    }//setPath():void


    List getAttributes(VRTestAttributeFilter filter) //null == return all
            throws VRTestException, IOException
    {
        if (filter == VRTestAttributeFilter.NO_ATTRIBUTES)
        {
            //return no attributes
            return Collections.EMPTY_LIST;
        }

        setAttributes();

        if (filter == VRTestAttributeFilter.ALL_ATTRIBUTES)
        {
            //return all attributes
            return attributes.attributes();
        }
        else
        {
            //return subset of attributes matching those in the filter

            VRTestAttributeList filteredAttributes;
            List filterAttributes;
            ListIterator fa, ma;
            VRTestAttribute filterAttribute, objectAttribute;
            LinkedList matches;
            VRTestAttributeSchema attributeSchema;

            getClassSchema();
            filteredAttributes = new VRTestAttributeList();
            filterAttributes = filter.attributes();
            filterAttribute = null;
            objectAttribute = null;

            fa = filterAttributes.listIterator();
            while (fa.hasNext())
            {
                filterAttribute = (VRTestAttribute) fa.next();

                attributeSchema = classSchema.getAttributeSchema(filterAttribute.getName());
                if (attributeSchema != null)
                {
                    //if the attribute name is legal
                    matches = attributes.getAttribute(attributeSchema);
                    if (matches != null)
                    {
                        ma = matches.listIterator();
                        while (ma.hasNext())
                        {
                            objectAttribute = (VRTestAttribute) ma.next();
                            filteredAttributes.addAttribute(objectAttribute);
                        }
                    }//if
                }//if
            }//while()

            return filteredAttributes.attributes();
        }//else
    } //getAttributes(VRTestAttributeFilter):List


    VRTestAttributeSchema getNamingAttributeSchema()
            throws VRTestException
    {
        if (namingAttributeSchema == null)
        {
            namingAttributeSchema = getClassSchema().getNamingAttributeSchema();
        }

        return namingAttributeSchema;
    }//getNamingAttributeSchema():VRTestAttributeSchema


    void resetAttributes()
    {
        attributes = VRTestAttributeList.EMPTY;
    }//resetAttributes()


    boolean isSubordinate(VRTestObject object)
            throws VRTestException, IOException
    {
        if (object == null)
        {
            throw new IllegalArgumentException
                    (Errors.nullParam("object"));
        }

        //a child object must be a child of the parent object and not the
        //	parent object itself
        if (this == object)
        {
            return false;
        }

        String parentDN, childDN;

        parentDN = getDN();
        childDN = object.getDN();

        if ((childDN == null) ||
                (parentDN == null))
        {
            throw new VRTestException
                    (Errors.NO_DN);
        }

        //in order to be a child object, the object's dn must contain
        //	the parent object's dn
        //
        //	e.g. \Organization\Novell  			(parent object)
        //		 \Organization\Novell\Provo		(child)
        //		 \Organization					(non-child)
        boolean prefixMatch;
        int parentLength = parentDN.length();
        prefixMatch = parentDN.regionMatches(true, //case insensitive
                                             FIRST_INDEX,
                                             childDN,
                                             FIRST_INDEX,
                                             parentLength);
        if (!prefixMatch)
        {
            return false;
        }

        //a child object must be an immediate child of the parent object
        //	(one level below the parent object)
        //
        //	e.g. \Organization\   						 	(parent object)
        //		 \Organization\Subordinate				 	(child)
        //		 \Organization\Subordinate\NonSubordinate	(non-child)
        //
        int lastDelimIndex = childDN.lastIndexOf(PATH_DELIMITER_CHAR);
        if (lastDelimIndex != (parentLength - 1))
        {
            return false;
        }

        return true;
    }//isSubordinate(VRTestObject):boolean


    boolean inSubtree(VRTestObject object)
            throws VRTestException, IOException
    {
        if (object == null)
        {
            throw new IllegalArgumentException
                    (Errors.nullParam("object"));
        }

        //if it's the same as the root object, then it's got to be in the subtree
        if (this == object)
        {
            return true;
        }

        String rootDN, subDN;

        rootDN = getDN();
        subDN = object.getDN();

        if ((rootDN == null) ||
                (subDN == null))
        {
            throw new VRTestException
                    (Errors.NO_DN);
        }


        if (subDN == null)
        {
            //if the object doesn't have a dn, we can't tell if it's a
            //	member of the root's tree
            return false;
        }

        //in order to be a member of the root object's tree, the object's dn
        //	must contain the root object's dn
        //
        //	e.g. \Organization\Novell  			(root object)
        //		 \Organization\Novell\Provo		(member)
        //		 \Organization					(non-member)
        boolean prefixMatch;
        int rootLength = rootDN.length();
        prefixMatch = rootDN.regionMatches(true, //case insensitive
                                           FIRST_INDEX,
                                           subDN,
                                           FIRST_INDEX,
                                           rootLength);
        if (!prefixMatch)
        {
            return false;
        }

        //in order to be a member of the root object's tree, the object can
        //	have either the root object's dn or extend the root object's dn
        //
        //	e.g. \Organization\  			(root object, member)
        //		 \Organizational			(non-member)
        //		 \Organization\Novell		(member)

        //since prefixes match, we know subDN is AT LEAST as long as the rootDN,
        //	thus it is extraneous to catch an IndexOutOfBoundsException
        if ((subDN.charAt(rootLength - 1) != PATH_DELIMITER_CHAR))
        {
            return false;
        }

        return true;
    }//inSubtree(VRTestObject):boolean

}//class VRTestObject


