AgentSet.java  
// $Id: AgentSet.java,v 1.1 2000/05/18 22:01:06 tucker Exp $
// Hive. Copyright (c) 1998-2000, The Massachusetts Institute of Technology.
// All rights reserved. Distributed with no warranty, without even the
// implied warranty of merchantability or fitness for a particular purpose.
// For more details see COPYING.GPL, the GNU General Public License.


package net.hivecell.hive.description;
import net.hivecell.hive.support.Debug;
import java.io.Serializable;
import java.util.Vector;
import java.util.Enumeration;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.io.Serializable;
import java.util.Random;
import java.util.Hashtable;

/** 
 ** A set of Descriptions.
 **
 ** This has much less functionality than the RDF DescSet, as all
 ** of the individual Description querying capability is in the
 ** Description class.  This is used for those situations 
 ** (Which seem to occur exclusively in the Cell resource discovery)
 ** where a bunch of descriptions needs to be manipulated at once.
 ** Thus, this can manipulate many Descriptions at once, and query
 ** those descriptions to see which ones match certain parameters,
 ** but individual Description querying must be done thru the 
 ** Description ( xmlDescSet.getDesc(i) ).
 **/


/**
 ** NEW VISUALIZATION: 2/27/00, tucker
 ** This is an AgentSet, which stores a group of Agents, 
 ** and has methods for querying them based on their
 ** descriptions.  The reason it is now focused on the 
 ** agent as opposed to the description is to guarantee 
 ** that the agent and its description will always be in 
 ** sync.  Currently, the set was stored by the 
 ** description, so that if an agent changed its 
 ** description, that would not be reflected in the set.
 ** This way the description will always be in sync, as 
 ** it is referenced through its agent.  Also, this allows
 ** for agents with null descriptions to be included in 
 ** set.
 **/

public class AgentSet 
implements Cloneable, Serializable  {


    Vector agents = new Vector(); // Describable's represented in this set
    static Random rand = new Random();  // A little bit of non-determinism for the pickOne function


    public AgentSet() {
    super();
    }


    /**
     ** Makes AgentSet of desribables
     **/
    public AgentSet(Vector objs) {
    Enumeration e = objs.elements();
    while (e.hasMoreElements()) {
        Object o = e.nextElement();
        if (o instanceof RemoteDescribable)
        agents.addElement( o );
        else {
        Debug.error("Attempt to add a non-Describable to an AgentSet!  Skipping it...");
        continue;
        }
    }
    }


    /**
     ** Makes AgentSet of desribables
     **/
    public AgentSet(Object[] objs) {
    for (int i = 0; i < objs.length; i++) {
        if (objs[i] instanceof RemoteDescribable)
        agents.addElement( objs[i] );
        else {
        Debug.error("Attempt to add a non-Describable to an AgentSet!  Skipping it...");
        continue;
        }
    }
    }


    /**
     ** Adds a new Describable to the AgentSet.
     ** Doesn't check for duplicates.  Should it?
     **/
    public void addDesc(RemoteDescribable desc) {
    agents.addElement(desc);
    }


    /**
     ** How many Descriptions in the AgentSet
     **/
    public int size() { return (agents.size()); }



    /**
     ** Grab an agent from the set
     **/
    public Object getObject(int which) { return (agents.elementAt(which)); }



    /**
     ** Return the names of all the Descriptions in the set.
     **/
    public Vector getNames() {
    Vector ret = new Vector();
    
    Enumeration e = agents.elements();
    while (e.hasMoreElements()) {
        Description desc;
        try {
        desc = ((RemoteDescribable)e.nextElement()).getDescription();
        ret.addElement(desc.getName());

        } catch( RemoteException error ) {
        Debug.error( error );

        }
    }
    return (ret);
    }

    /**
     ** Returns an Enumeration of the agents which belong
     ** to the descriptions.
     **/
    public Enumeration elements() {
    return (agents.elements());
    }


    /** Select a random agent (or cell or shadow) belonging
     ** to one of the Descriptions contained in this
     ** AgentSet.
     **/
    public Object pickOne() {
    if (agents.size() == 0) return (null);

    int at = 0;
    if (agents.size() > 1) 
        at = (int)( rand.nextDouble() * ((double)agents.size()) );

    return ( agents.elementAt(at) );
    }



    
    public boolean contains(Object o) {
    Enumeration e = this.elements();
    while (e.hasMoreElements())
        if (e.nextElement().equals(o)) return true;
    return false;
    }

    
    public Description getDescForObject(Object o) {
    Enumeration e = agents.elements();
    while (e.hasMoreElements()) {
        RemoteDescribable ag = (RemoteDescribable)e.nextElement();
        try {
        if ( o.equals(ag) )
            return (ag.getDescription());

        } catch( RemoteException error ) {
        Debug.error( error );

        }

    }
    return null;
    }



    /**
     ** Shorthand form for when you don't want to build an entire array for just
     ** one syntactic, semantic attribute.
     **/
    public AgentSet querySet(String syntactic, String semantic) {
    String[] synArr = null;
    if( syntactic != null ) {
        synArr = new String[1];
        synArr[0] = syntactic;

    }

    String[] semArr = null;
    if( semantic != null ) {
        semArr = new String[1];
        semArr[0] = semantic;

    }

    return querySet( synArr, semArr );

    }

    /**
     * This runs the queries on the Descriptions, and returns a new
     * AgentSet containing only those Descriptions that matched:
     * ie. all agents that are all of the types listed in syntactic,
     * AND contain all of the tags listed in syntactic are returned
     *
     * @param syntatic an array of syntatic stuff - null if we don't need it
     * @param semantic the array of semantic tags we are looking for
     * @return the desc set that contains everything that we matched
     */
    public AgentSet querySet( String[] syntactic, String[] semantic ) {
    Vector syn = new Vector(); // These are the agents that passed the syntactic test.

    // stubs will not implement the Implementation of the agent,
    // so we need to keep track and also get the interfaces and
    // see if we can do it
    if( syntactic != null ) {
        Enumeration agentsEnumeration = this.elements(); // the list of all the agent
    syntacticSearch:
        while( agentsEnumeration.hasMoreElements() ) {
        Object o = agentsEnumeration.nextElement();
        syntaticSubSearch:
        for(int i = 0; i < syntactic.length; i++) {    
            try {
            // create a list of classes that we can test
            // to see if we can assignable from it
            Class requestedClass = Class.forName( syntactic[i] );
            Vector searchingClasses = new Vector();
            searchingClasses.addElement( requestedClass );
            if( !requestedClass.isInterface() ) {
                Class[] possibleClasses = requestedClass.getInterfaces();
                for( int count=0;count<possibleClasses.length;count++ )
                searchingClasses.addElement( possibleClasses[count] );

            }

            // if this class is assignable, then woo-hoo!
            // we have to test to see if we match the
            // other syntatic stuff
            Enumeration e = searchingClasses.elements();
            while( e.hasMoreElements() ) {
                Class searchingClass = (Class)e.nextElement();
                    if( searchingClass.isAssignableFrom( o.getClass() ) )
                continue syntaticSubSearch;

            }
            continue syntacticSearch; // otherwise, punt this

            } catch( ClassNotFoundException c ) {
            Debug.notice( "Got a class not found exception when looking for a syntactic match.", c );
            continue syntacticSearch;
            }
        }
        
        // Matched all the syntactic requirements, put it in group to be tested for semantic match.
        syn.addElement( o );

        }

    } else // syntactic == null, just test semantics.
        syn = agents;

    // Now, test out the syns to see which match the semantic description:
    AgentSet ret = new AgentSet();
    if( semantic != null ) {
        Enumeration e = syn.elements();
    semanticSearch:
        while( e.hasMoreElements() ) {
        Description desc = null;
        RemoteDescribable obj = (RemoteDescribable)e.nextElement();
        try {
            desc = obj.getDescription();
            desc = desc.semanticContext();
            
        } catch( RemoteException error ) {
            Debug.error( error );

        }

        if( desc != null ) {
            for( int i=0;i<semantic.length;i++ )
            if( !desc.selectFirst( semantic[i] ) )
                continue semanticSearch;
            ret.addDesc( obj );   // All semantic tags found!
        }
        }

    } else { // semantic == null, return result of syntactic test.
        Enumeration e = syn.elements();
        while( e.hasMoreElements() )
        ret.addDesc( (RemoteDescribable)e.nextElement() );

    }

    return ret;

    }


    /**
     ** This runs the queries on the Descriptions, and returns a new
     ** AgentSet containing only those Descriptions that matched.
     **
     ** Just works on a single tag/value pair for now.  Ability to do multiple
     ** means re-writing a different but similar querySet.
     **/
    /*
      If extreme efficiency is a concern, this could be improved by making
      this a slightly modified version of querySet:
      use containsTagValue in place of selectFirst().  This avoids searching the
      Tree twice for tags by doing the tag lookup and value compare all at once.
      This way is neater and more portable tho.
     */
    public AgentSet queryValues(String syntactic, String semantic, String semVal) {
    AgentSet set = querySet(syntactic, semantic);
    if (set.size() == 0) return set;


    AgentSet ret = new AgentSet();
    Enumeration descs = set.elements();
    while (descs.hasMoreElements()) {
        Description desc = null;
        RemoteDescribable o = (RemoteDescribable)descs.nextElement();
        try {
        desc = o.getDescription();

        } catch( RemoteException error ) {
        Debug.error( error );

        }
        if ( desc != null && desc.containsTagValue(semantic, semVal) ) ret.addDesc(o);
    }

    return (ret);
    }



    /**
     ** This runs the queries on the Descriptions, and returns a new
     ** AgentSet containing only those Descriptions that matched.
     **
     ** It returns all agents that match all of the syntactic requirements,
     ** contain all of the tags in semantic, and each tag has the value which 
     ** is the corresponding value in semVals.
     **/
    /*
      If extreme efficiency is a concern, this could be improved by making
      this a slightly modified version of querySet:
      use containsTagValue in place of selectFirst().  This avoids searching the
      Tree twice for tags by doing the tag lookup and value compare all at once.
      This way is neater and more portable tho.
     */
    public AgentSet queryValues(String syntactic[], String semantic[], String semVals[]) {
    if (semantic.length != semVals.length) {
        Debug.warning("Error in query values: semantic String[] and semanticValues String[] must have the same number of elements");
        return (new AgentSet());
    }
    AgentSet set = querySet(syntactic, semantic);
    if (set.size() == 0) return set;


    AgentSet ret = new AgentSet();
    Enumeration descs = set.elements();
    while (descs.hasMoreElements()) {
        Description desc = null;
        RemoteDescribable o = (RemoteDescribable)descs.nextElement();
        try {
        desc = o.getDescription();

        } catch( RemoteException error ) {
        Debug.error( error );

        }

        if (desc != null) {
        boolean containsAllVals = true;
        for (int i = 0; i < semantic.length; i++)
            if (!desc.containsTagValue(semantic[i], semVals[i])) containsAllVals = false;
        
        if (containsAllVals) ret.addDesc(o);
        }
    }

    return (ret);
    }
}