Package jade.content.schema

Source Code of jade.content.schema.ObjectSchemaImpl

/**
* ***************************************************************
* JADE - Java Agent DEvelopment Framework is a framework to develop
* multi-agent systems in compliance with the FIPA specifications.
* Copyright (C) 2000 CSELT S.p.A.
*
* GNU Lesser General Public License
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation,
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA  02111-1307, USA.
* **************************************************************
*/
package jade.content.schema;

import jade.content.onto.*;
import jade.content.abs.*;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;
import jade.content.schema.facets.*;
import jade.core.CaseInsensitiveString;
import jade.util.leap.Serializable;
import jade.util.Logger;

/**
* @author Giovanni Caire - TILAB
*/
class ObjectSchemaImpl extends ObjectSchema {
  private Logger logger = Logger.getMyLogger(this.getClass().getName());

    static final String RESULT_SLOT_NAME = "__Result_SLOT_123";
   
  private class SlotDescriptor implements Serializable {
    private String       name = null;
    private ObjectSchema schema = null;
    private int          optionality = 0;
    /**
     Construct a SlotDescriptor
     */
    private SlotDescriptor(String name, ObjectSchema schema, int optionality) {
      this.name = name;
      this.schema = schema;
      this.optionality = optionality;
    }

  }


  private String          typeName = null;
  private Hashtable       slots;
  private Vector         slotNames;
  private Vector          superSchemas;
 
  // Note that the list of facets for a given slot cannot be included in the slot descriptor.
  // In fact facets are associated to the a slot in a given schema, not to the slot itself.
  // For instance if we have
  // - schema A that extends schema B (B is a super-schema of A)
  // - B defines slot S and associates facet f1 to it
  // - A associates facet f2 to slot S
  // When validating an instance of B only facet f1 must be applied, while when validating
  // an instance of A both f1 and f2 must be applied.
 
  private Hashtable       facets;

  static {
    baseSchema = new ObjectSchemaImpl();
  }

  /**
   * Construct a schema that vinculates an entity to be a generic
   * object (i.e. no constraints at all)
   */
  private ObjectSchemaImpl() {
    this(BASE_NAME);
  }

  /**
   * Creates an <code>ObjectSchema</code> with a given type-name.
   * @param typeName The name of this <code>ObjectSchema</code>.
   */
  protected ObjectSchemaImpl(String typeName) {
    this.typeName = typeName;
  }

  /**
   * Add a slot to the schema.
   * @param name The name of the slot.
   * @param slotSchema The schema defining the type of the slot.
   * @param optionality The optionality, i.e., <code>OPTIONAL</code>
   * or <code>MANDATORY</code>
   */
  protected void add(String name, ObjectSchema slotSchema, int optionality) {
    CaseInsensitiveString ciName = new CaseInsensitiveString(name);
    if (slots == null) {
      slots = new Hashtable();
      slotNames = new Vector();
    }
    if (slots.put(ciName, new SlotDescriptor(name, slotSchema, optionality)) == null) {
      // We treat Action results as if they were slots. However we don't want the special
      // RESULT_SLOT_NAME to be included among slot names
      if (!name.equals(RESULT_SLOT_NAME)) {
        slotNames.addElement(ciName);
      }
    }
  }

  /**
   * Add a mandatory slot to the schema.
   * @param name name of the slot.
   * @param slotSchema schema of the slot.
   */
  protected void add(String name, ObjectSchema slotSchema) {
    add(name, slotSchema, MANDATORY);
  }

  /**
   * Add a slot with cardinality between <code>cardMin</code>
   * and <code>cardMax</code> to this schema.
   * Adding such a slot corresponds to add a slot
   * of type Aggregate and then to add proper facets (constraints)
   * to check that the type of the elements in the aggregate are
   * compatible with <code>elementsSchema</code> and that the
   * aggregate contains at least <code>cardMin</code> elements and
   * at most <code>cardMax</code> elements. By default the Aggregate
   * is of type <code>BasicOntology.SEQUENCE</code>.
   * @param name The name of the slot.
   * @param elementsSchema The schema for the elements of this slot.
   * @param cardMin This slot must get at least <code>cardMin</code>
   * values
   * @param cardMax This slot can get at most <code>cardMax</code>
   * values
   */
  protected void add(String name, ObjectSchema elementsSchema, int cardMin, int cardMax) {
    add(name, elementsSchema, cardMin, cardMax, BasicOntology.SEQUENCE);
  }

  /**
   * Add a slot with cardinality between <code>cardMin</code>
   * and <code>cardMax</code> to this schema and allow specifying the type
   * of Aggregate to be used for this slot.
   * @param name The name of the slot.
   * @param elementsSchema The schema for the elements of this slot.
   * @param cardMin This slot must get at least <code>cardMin</code>
   * values
   * @param cardMax This slot can get at most <code>cardMax</code>
   * values
   * @param aggType The type of Aggregate to be used
   * @see #add(String, ObjectSchema, int, int)
   */
  protected void add(String name, ObjectSchema elementsSchema, int cardMin, int cardMax, String aggType) {
    int optionality = (cardMin == 0 ? OPTIONAL : MANDATORY);
    add(name, elementsSchema, cardMin, cardMax, aggType, optionality);
  }

  /**
   * Add a slot with optionality and cardinality between <code>cardMin</code>
   * and <code>cardMax</code> to this schema and allow specifying the type
   * of Aggregate to be used for this slot.
   * @param name The name of the slot.
   * @param elementsSchema The schema for the elements of this slot.
   * @param cardMin This slot must get at least <code>cardMin</code>
   * values
   * @param cardMax This slot can get at most <code>cardMax</code>
   * values
   * @param aggType The type of Aggregate to be used
   * @param optionality The optionality, i.e., <code>OPTIONAL</code>
   * @see #add(String, ObjectSchema, int, int)
   */
  protected void add(String name, ObjectSchema elementsSchema, int cardMin, int cardMax, String aggType, int optionality) {
    try {
      // If the aggregate type is not yet present in the BasicOntology, add it (without elements type specification)
      ObjectSchema aggTypeSchema = BasicOntology.getInstance().getSchema(aggType);
      if (aggTypeSchema == null) {
        // Create a new aggregate schema and add it to BasicOntology
        aggTypeSchema = new AggregateSchema(aggType);
        BasicOntology.getInstance().add(aggTypeSchema);
      }
     
      // If elements are typed, we need an ad-hoc schema to hold the elements type specification
      ObjectSchema schema = null;
      if (elementsSchema != null) {
        schema = new AggregateSchema(aggType, (TermSchema) elementsSchema);
      }
      else {
        schema = aggTypeSchema;
      }
     
      add(name, schema, optionality);
     
      // Add proper facets
      if (elementsSchema != null) {
        addFacet(name, new TypedAggregateFacet(elementsSchema));
      }
      addFacet(name, new CardinalityFacet(cardMin, cardMax));
    }
    catch (OntologyException oe) {
      // Should never happen
      oe.printStackTrace();
    }
  }

  /**
   * Add a super schema to this schema, i.e. this schema will
   * inherit all characteristics from the super schema
   * @param superSchema the super schema.
   */
  protected void addSuperSchema(ObjectSchema superSchema) {
    if (superSchema != null) {
      if (superSchemas == null) {
        superSchemas = new Vector();
      }
      superSchemas.addElement(superSchema);
    }
  }

  /**
   Add a <code>Facet</code> on a slot of this schema
   @param slotName the name of the slot the <code>Facet</code>
   must be added to.
   @param f the <code>Facet</code> to be added.
   @throws OntologyException if slotName does not identify
   a valid slot in this schema
   */
  protected void addFacet(String slotName, Facet f) throws OntologyException {
    if (containsSlot(slotName)) {
      CaseInsensitiveString ciName = new CaseInsensitiveString(slotName);
      if (facets == null) {
        facets = new Hashtable();
      }
      Vector v = (Vector) facets.get(ciName);
      if (v == null) {
        v = new Vector();
        facets.put(ciName, v);
        //DEBUG
        if(logger.isLoggable(Logger.CONFIG))
          logger.log(Logger.CONFIG,"Added facet "+f+" to slot "+slotName);
      }
      v.addElement(f);
    }
    else {
      throw new OntologyException(slotName+" is not a valid slot in this schema");
    }
  }

  /**
   * Retrieves the name of the type of this schema.
   * @return the name of the type of this schema.
   */
  public String getTypeName() {
    return typeName;
  }

  /**
   * Returns the names of all the slots in this <code>Schema</code>
   * (including slots defined in super schemas).
   *
   * @return the names of all slots.
   */
  public String[] getNames() {
    Vector allSlotNames = new Vector();

    fillAllSlotNames(allSlotNames);

    String[] names = new String[allSlotNames.size()];
    int      counter = 0;
    for (Enumeration e = allSlotNames.elements(); e.hasMoreElements(); ) {
      names[counter++] = ((CaseInsensitiveString) e.nextElement()).toString();
    }

    return names;
  }
 
  /**
   * Returns the names of the slots defined in this <code>Schema</code>
   * (excluding slots defined in super schemas).
   *
   * @return the names of the slots defined in this <code>Schema</code>.
   */
  public String[] getOwnNames() {
    if (slotNames != null) {
      String[] ownNames = new String[slotNames.size()];
      int counter = 0;
      for (Enumeration e = slotNames.elements(); e.hasMoreElements(); ) {
        ownNames[counter++] = ((CaseInsensitiveString) e.nextElement()).toString();
      }
      return ownNames;
    }
    else {
      return new String[0];
    }
  }

  /**
   * Retrieves the schema of a slot of this <code>Schema</code>.
   *
   * @param name The name of the slot.
   * @return the <code>Schema</code> of slot <code>name</code>
   * @throws OntologyException If no slot with this name is present
   * in this schema.
   */
  public ObjectSchema getSchema(String name) throws OntologyException {
    SlotDescriptor slot = getSlot(new CaseInsensitiveString(name));
    if (slot == null) {
      throw new OntologyException("No slot named: " + name);
    }
    return slot.schema;
  }

  /**
   * Indicate whether a given <code>String</code> is the name of a
   * slot defined in this <code>Schema</code>
   *
   * @param name The <code>String</code> to test.
   * @return <code>true</code> if <code>name</code> is the name of a
   * slot defined in this <code>Schema</code>.
   */
  public boolean containsSlot(String name) {
    SlotDescriptor slot = getSlot(new CaseInsensitiveString(name));
    return (slot != null);
  }

  /**
   * Indicate whether a given <code>String</code> is the name of a
   * slot actually defined in this <code>Schema</code> (excluding super-schemas)
   *
   * @param name The <code>String</code> to test.
   * @return <code>true</code> if <code>name</code> is the name of a
   * slot actually defined in this <code>Schema</code> (excluding super-schemas).
   */
  public boolean isOwnSlot(String name) {
    SlotDescriptor slot = getOwnSlot(new CaseInsensitiveString(name));
    return (slot != null);
  }
 
  /**
   * Indicate whether a slot of this schema is mandatory
   *
   * @param name The name of the slot.
   * @return <code>true</code> if the slot is mandatory.
   * @throws OntologyException If no slot with this name is present
   * in this schema.
   */
  public boolean isMandatory(String name) throws OntologyException {
    SlotDescriptor slot = getSlot(new CaseInsensitiveString(name));
    if (slot == null) {
      throw new OntologyException("No slot named: " + name);
    }
    return slot.optionality == MANDATORY;
  }

  /**
   * Creates an Abstract descriptor to hold an object compliant to
   * this <code>Schema</code>.
   */
  public AbsObject newInstance() throws OntologyException {
    throw new OntologyException("AbsObject cannot be instantiated");
  }

  private final void fillAllSlotNames(Vector v) {
    // Get slot names of super schemas (if any) first
    if (superSchemas != null) {
      for (Enumeration e = superSchemas.elements(); e.hasMoreElements(); ) {
        ObjectSchemaImpl superSchema = (ObjectSchemaImpl) e.nextElement();

        superSchema.fillAllSlotNames(v);
      }
    }

    // Then add slot names of this schema
    if (slotNames != null) {
      for (Enumeration e = slotNames.elements(); e.hasMoreElements(); ) {
        v.addElement(e.nextElement());
      }
    }
  }

  /**
   Check whether a given abstract descriptor complies with this
   schema.
   @param abs The abstract descriptor to be checked
   @throws OntologyException If the abstract descriptor does not
   complies with this schema
   */
  public void validate(AbsObject abs, Ontology onto) throws OntologyException {
    validateSlots(abs, onto);
  }

  /**
   For each slot
   - get the corresponding attribute value from the abstract descriptor
   abs
   - Check that it is not null if the slot is mandatory
   - Check that its schema is compatible with the schema of the slot
   - Check that it is a correct abstract descriptor by validating it
   against its schema.
   */
  protected void validateSlots(AbsObject abs, Ontology onto) throws OntologyException {
    // Validate all the attributes in the abstract descriptor
    String[] slotNames = getNames();
    for (int i = 0; i < slotNames.length; ++i) {
      AbsObject slotValue = abs.getAbsObject(slotNames[i]);
      CaseInsensitiveString ciName = new CaseInsensitiveString(slotNames[i]);
      validate(ciName, slotValue, onto);
    }
  }

  /**
   Validate a given abstract descriptor as a value for a slot
   defined in this schema
   @param slotName The name of the slot
   @param value The abstract descriptor to be validated
   @throws OntologyException If the abstract descriptor is not a
   valid value
   @return true if the slot is defined in this schema (or in
   one of its super schemas). false otherwise
   */
  private boolean validate(CaseInsensitiveString slotName, AbsObject value, Ontology onto) throws OntologyException {
    // DEBUG
    if(logger.isLoggable(Logger.FINE))
      logger.log(Logger.FINE,"Validating "+(value != null ? value.toString() : "null")+" as a value for slot "+slotName);
    // NOTE: for performance reasons we don't want to scan the schema
    // to check if slotValue is a valid slot and THEN to scan again
    // the schema to validate value. This is the reason for the
    // boolean return value of this method
    boolean slotFound = false;

    // If the slot is defined in this schema --> check the value
    // against the schema of the slot. Otherwise let the super-schema
    // where the slot is defined validate the value
    SlotDescriptor dsc = getOwnSlot(slotName);
    if (dsc != null) {
      // DEBUG
      if(logger.isLoggable(Logger.CONFIG))
        logger.log(Logger.CONFIG,"Slot "+slotName+" is defined in schema "+this);
      if (value == null) {
        // Check optionality
        if (dsc.optionality == MANDATORY) {
          throw new OntologyException("Missing value for mandatory slot "+slotName+". Schema is "+this);
        }
        // Don't need to check facets on a null value for an optional slot
        return true;
      }
      else {
        // - Get from the ontology the schema s that defines the type
        // of the abstract descriptor value.
        // - Check if this schema is compatible with the schema for
        // slot slotName
        // - Finally check value against s
        ObjectSchema s = onto.getSchema(value.getTypeName());
        //DEBUG
        if(logger.isLoggable(Logger.CONFIG))
          logger.log(Logger.CONFIG,"Actual schema for "+value+" is "+s);
        if (s == null) {
          throw new OntologyException("No schema found for type "+value.getTypeName()+". Ontology is "+onto.getName());
        }
        if (!s.isCompatibleWith(dsc.schema)) {
          throw new OntologyException("Schema "+s+" for element "+value+" is not compatible with schema "+dsc.schema+" for slot "+slotName);
        }
        //DEBUG
        if(logger.isLoggable(Logger.CONFIG))
          logger.log(Logger.CONFIG,"Schema "+s+" for type "+value+" is compatible with schema "+dsc.schema+" for slot "+slotName);
        s.validate(value, onto);
      }
      slotFound = true;
    }
    else {
      if (superSchemas != null) {
        Enumeration e = superSchemas.elements();
        while (e.hasMoreElements()) {
          ObjectSchemaImpl s = (ObjectSchemaImpl) e.nextElement();
          if (s.validate(slotName, value, onto)) {
            slotFound = true;
            // Don't need to check other super-schemas
            break;
          }
        }
      }
    }

    if (slotFound && facets != null) {
      // Check value against the facets (if any) defined for the
      // slot in this schema
      Vector ff = (Vector) facets.get(slotName);
      if (ff != null) {
        Enumeration e = ff.elements();
        while (e.hasMoreElements()) {
          Facet f = (Facet) e.nextElement();
          //DEBUG
          if(logger.isLoggable(Logger.CONFIG))
            logger.log(Logger.CONFIG,"Checking facet "+f+" defined on slot "+slotName);
          f.validate(value, onto);
        }
      }
      else {
        //DEBUG
        if(logger.isLoggable(Logger.CONFIG))
          logger.log(Logger.CONFIG,"No facets for slot "+slotName);
      }
    }

    return slotFound;
  }

  /**
   Check if this schema is compatible with a given schema s.
   This is the case if
   1) This schema is equals to s
   2) s is one of the super-schemas of this schema
   3) This schema descends from s i.e.
   - s is the base schema for the XXXSchema class this schema is
   an instance of (e.g. s is ConceptSchema.getBaseSchema() and this
   schema is an instance of ConceptSchema)
   - s is the base schema for a super-class of the XXXSchema class
   this schema is an instance of (e.g. s is TermSchema.getBaseSchema()
   and this schema is an instance of ConceptSchema)
   */
  public boolean isCompatibleWith(ObjectSchema s) {
    if (equals(s)) {
      return true;
    }
    if (isSubSchemaOf(s)) {
      return true;
    }
    if (descendsFrom(s)) {
      return true;
    }
    return false;
  }

  /**
   Return true if
   - s is the base schema for the XXXSchema class this schema is
   an instance of (e.g. s is ConceptSchema.getBaseSchema() and this
   schema is an instance of ConceptSchema)
   - s is the base schema for a super-class of the XXXSchema class
   this schema is an instance of (e.g. s is TermSchema.getBaseSchema()
   and this schema is an instance of ConceptSchema)
   */
  protected boolean descendsFrom(ObjectSchema s) {
    // The base schema for the ObjectSchema class descends only
    // from itself
    if (s!= null) {
      return s.equals(getBaseSchema());
    }
    else {
      return false;
    }
  }

  /**
   Return true if s is a super-schema (directly or indirectly)
   of this schema
   */
  private boolean isSubSchemaOf(ObjectSchema s) {
    if (superSchemas != null) {
      Enumeration e = superSchemas.elements();
      while (e.hasMoreElements()) {
        ObjectSchemaImpl s1 = (ObjectSchemaImpl) e.nextElement();
        if (s1.equals(s)) {
          return true;
        }
        if (s1.isSubSchemaOf(s)) {
          return true;
        }
      }
    }
    return false;
  }

  public String toString() {
    return getClass().getName()+"-"+getTypeName();
  }

  public boolean equals(Object o) {
    if (o != null) {
      return toString().equals(o.toString());
    }
    else {
      return false;
    }
  }

  public ObjectSchema[] getSuperSchemas() {
    ObjectSchema[] ss = null;
    if (superSchemas != null) {
      ss = new ObjectSchema[superSchemas.size()];
      for (int i = 0; i < ss.length; ++i) {
        ss[i] = (ObjectSchema) superSchemas.elementAt(i);
      }
    }
    else {
      ss = new ObjectSchema[0];
    }
    return ss;
  }
 
  public Facet[] getFacets(String slotName) {
    Vector v = getAllFacets(slotName);
    Facet[] ff = null;
    if (v != null) {
      ff = new Facet[v.size()];
      for (int i = 0; i < v.size(); ++i) {
        ff[i] = (Facet) v.elementAt(i);
      }
    }
    return ff;
  }
 
  /**
   * Get the facets defined upon a slot. More in details this method returns
   * all facets defined in this schema plus all facets defined in super-schemas
   * up to the schema actually declaring the given slot.
   * @return the facets defined upon a slot or null if the specified slot is not found.
   */
  Vector getAllFacets(String slotName) { // Package-scoped for testing purposes only
    Vector allFacets = new Vector();
    CaseInsensitiveString caseInsensitiveSlotName = new CaseInsensitiveString(slotName);
    if (facets != null) {
      Vector v = (Vector)facets.get(caseInsensitiveSlotName);
      if (v != null) {
        // We don't use Vector.addAll() for MIDP compatibility
        addAll(allFacets, v);
      }
    }
   
    boolean found = false;
    if (getOwnSlot(caseInsensitiveSlotName) == null) {
      // The slot must be defined in one of the super-schema
      if (superSchemas!=null) {
        for (int i = 0; i < superSchemas.size(); i++) {
          ObjectSchemaImpl superSchema = (ObjectSchemaImpl) superSchemas.elementAt(i);
          if (superSchema.containsSlot(slotName)) {
            found = true;
            // We don't use Vector.addAll() for MIDP compatibility
            addAll(allFacets, superSchema.getAllFacets(slotName));
          }
        }
      }
    }
    else {
      found = true;
    }
   
    return (found ? allFacets : null);
  }

  private final void addAll(Vector v1, Vector v2) {
    for (int i = 0; i < v2.size(); ++i) {
      v1.addElement(v2.elementAt(i));
    }
  }
 
  private final SlotDescriptor getOwnSlot(CaseInsensitiveString ciName) {
    return (slots != null ? (SlotDescriptor) slots.get(ciName) : null);
  }

  private final SlotDescriptor getSlot(CaseInsensitiveString ciName) {
    SlotDescriptor dsc = getOwnSlot(ciName);
    if (dsc == null) {
      if (superSchemas != null) {
        for (int i = 0; i < superSchemas.size(); ++i) {
          ObjectSchemaImpl sc = (ObjectSchemaImpl) superSchemas.elementAt(i);
          dsc = sc.getSlot(ciName);
          if (dsc != null) {
            break;
          }
        }
      }
    }
    return dsc;
  }

  public boolean isAssignableFrom(ObjectSchema s) {
    // This = destination schema
    // s = source schema
   
    // Type names must be equals or
    // the source schema must have a super-schema with the same type name of the destination type name
    // e.g.
    // - source schema = LivingBeing
    // - destination = Person and Person has a super-schema called LivingBeing
    if (s.isCompatibleWith(this)) {
      return true;
    }
   
    try {
      // All slots of the destination schema that are present in the source schema too must be assignable.
      // Slots of the destination schema not present in the source schema must be OPTIONAL.
      ObjectSchema destSchema;
      ObjectSchema srcSchema;
      String[] destSlotNames = getNames();
      for (int i = 0; i < destSlotNames.length; i++) {
        String destSlotName = destSlotNames[i];
        destSchema = getSchema(destSlotName);
       
        try {
          srcSchema = s.getSchema(destSlotName);
 
          // The slot is present in source and destination schema -> check compatibility
          if (!destSchema.isAssignableFrom(srcSchema)) {
            return false;
          }
        } catch (OntologyException e) {
          // The slot is present only in destination schema -> must be not mandatory
          if (isMandatory(destSlotName)) {
            return false;
          }
        }
      }
     
      return true;
     
    } catch (OntologyException e) {
      return false;
    }
  }
 
}

TOP

Related Classes of jade.content.schema.ObjectSchemaImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.