/* A concept that represents the concept values of entries in a record token.
*
* Copyright (c) 2010-2014 The Regents of the University of California. All
* rights reserved.
*
* Permission is hereby granted, without written agreement and without license
* or royalty fees, to use, copy, modify, and distribute this software and its
* documentation for any purpose, provided that the above copyright notice and
* the following two paragraphs appear in all copies of this software.
*
* IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
* CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
* "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE
* MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
package ptolemy.data.ontologies;
import java.util.Set;
import java.util.TreeSet;
import ptolemy.graph.CPO;
import ptolemy.graph.CPO.BoundType;
import ptolemy.kernel.util.IllegalActionException;
import ptolemy.kernel.util.InternalErrorException;
import ptolemy.kernel.util.NameDuplicationException;
/** A concept that represents the concept values of entries in a record token.
*
* A conceptable model element such as a port or node in a Ptolemy expression
* could contain a token value that is a record data type. A record token
* is a token that is a collection of multiple token values of different
* types. For example, we might want to specify a record that indicates the
* (x,y) pixel position on a black-and-white screen, and also true or false
* for whether that pixel position is on or off. We can use a record of the form:
* {x = 34, y = 26, pixelOn = true}
*
* This RecordConcept allows any record to be assigned concept values for its
* individual elements from an arbitrary finite ontology. For example, if we
* wanted to assign a concept to the token above from the constAbstractInterpretation
* ontology, it would be:
* {x = Positive, y = Positive, pixelOn = BooleanTrue}
*
* This code is adapted from the
* {@link ptolemy.data.ontologies.lattice.adapters.monotonicityAnalysis.MonotonicityConcept}
* implementation.
*
* @author Charles Shelton, Ben Lickly, Elizabeth Latronico
* @version $Id: RecordConcept.java 70402 2014-10-23 00:52:20Z cxh $
* @since Ptolemy II 10.0
* @Pt.ProposedRating Red (blickly)
* @Pt.AcceptedRating Red (blickly)
*/
public class RecordConcept extends MapTypeInfiniteConcept {
///////////////////////////////////////////////////////////////////
//// public constructors/factories ////
/** Create a new record concept, belonging to the given
* ontology, with an automatically generated name.
*
* @param ontology The ontology to which this concept belongs.
* @return The newly created RecordConcept.
* @exception InternalErrorException If there .
*/
public static RecordConcept createRecordConcept(Ontology ontology) {
try {
return new RecordConcept(ontology);
} catch (NameDuplicationException e) {
throw new InternalErrorException(
"Name conflict with automatically generated infinite concept name.\n"
+ "This should never happen.\n"
+ "Original exception:" + e.toString());
} catch (IllegalActionException e) {
throw new InternalErrorException(
"There was an error creating a new RecordConcept"
+ "in the " + ontology + "ontology\n."
+ "Original exception:" + e.toString());
}
}
///////////////////////////////////////////////////////////////////
//// public methods ////
/** Compare this concept with the given concept.
* Returns an int value that corresponds to the ordering between
* the elements as given in the CPO interface.
*
* @param concept The concept with which we are comparing.
* @return CPO.HIGHER if this concept is above the given concept,
* CPO.LOWER if this concept is below the given concept,
* CPO.SAME if both concepts are the same,
* and CPO.INCOMPARABLE if concepts are incomparable.
* @exception IllegalActionException If the specified concept
* does not have the same ontology as this one.
* @see ptolemy.data.ontologies.Concept#isAboveOrEqualTo(ptolemy.data.ontologies.Concept)
*/
@Override
public int compare(Concept concept) throws IllegalActionException {
if (concept.getOntology() == null
|| !concept.getOntology().equals(getOntology())) {
throw new IllegalActionException(this,
"Attempt to compare elements from two distinct ontologies");
}
// Original bottom and top remain bottom and top.
if (concept.equals(getOntology().getConceptGraph().bottom())) {
return CPO.HIGHER;
} else if (concept.equals(getOntology().getConceptGraph().top())) {
return CPO.LOWER;
} else if (!(concept instanceof RecordConcept)) {
return CPO.INCOMPARABLE;
}
RecordConcept righthandSide = (RecordConcept) concept;
CPO graph = getOntology().getConceptGraph();
boolean isSuperset = keySet().containsAll(righthandSide.keySet());
boolean isSubset = righthandSide.keySet().containsAll(keySet());
if (!isSubset && !isSuperset) {
return CPO.INCOMPARABLE;
}
Set commonFields = _commonKeys(righthandSide);
boolean seenHigher = false;
boolean seenLower = false;
boolean seenIncomparable = false;
for (String field : commonFields) {
int result = graph.compare(getConcept(field),
righthandSide.getConcept(field));
switch (result) {
case CPO.HIGHER:
seenHigher = true;
break;
case CPO.LOWER:
seenLower = true;
break;
case CPO.INCOMPARABLE:
seenIncomparable = true;
break;
case CPO.SAME:
break;
default:
throw new IllegalActionException(this, "ConceptGraph compare "
+ "did not return one of the defined CPO values. "
+ "Return value was " + result + ". This should "
+ "never happen.");
}
}
// Following the Ptolemy type system conventions, a record that has a superset
// of fields is lower in the lattice, and a record that has a subset of fields
// is higher in the lattice.
if (!seenHigher && !seenLower && !seenIncomparable && isSubset
&& isSuperset) {
return CPO.SAME;
} else if (!seenLower && !seenIncomparable && isSubset) {
return CPO.HIGHER;
} else if (!seenHigher && !seenIncomparable && isSuperset) {
return CPO.LOWER;
} else {
return CPO.INCOMPARABLE;
}
}
/** Compute the greatest lower bound (GLB) of this and another concept.
*
* @param concept The other concept
* @return The concept that is the GLB of this and the given concept.
*/
@Override
public Concept greatestLowerBound(Concept concept) {
return _getBoundWithOtherConcept(concept, BoundType.GREATESTLOWER);
}
/** Compute the least upper bound (LUB) of this and another concept.
*
* @param concept The other concept
* @return The concept that is the LUB of this and the given concept.
*/
@Override
public Concept leastUpperBound(Concept concept) {
return _getBoundWithOtherConcept(concept, BoundType.LEASTUPPER);
}
///////////////////////////////////////////////////////////////////
//// private methods ////
/** Return the concept that is the correct bound for the given two
* concepts.
* @param concept1 The first concept.
* @param concept2 The second concept.
* @param boundType Specifies the type of bound to be returned; either
* GREATESTLOWER or LEASTUPPER.
* @return The concept that is either the least upper bound or greatest
* lower bound of the two concepts.
*/
private Concept _getBoundFromConceptGraph(Concept concept1,
Concept concept2, BoundType boundType) {
ConceptGraph conceptGraph = getOntology().getConceptGraph();
switch (boundType) {
case GREATESTLOWER:
return conceptGraph.greatestLowerBound(concept1, concept2);
case LEASTUPPER:
return conceptGraph.leastUpperBound(concept1, concept2);
default:
throw new IllegalArgumentException("Unrecognized bound type: "
+ boundType + ". Expected either GREATESTLOWER or "
+ "LEASTUPPER");
}
}
/** Compute either the least upper bound or the greatest lower bound of
* this and another concept.
*
* @param concept The other concept.
* @param boundType Specifies the type of bound to be returned; either
* GREATESTLOWER or LEASTUPPER.
* @return The concept that is the bound of this and the given concept.
*/
private Concept _getBoundWithOtherConcept(Concept concept,
BoundType boundType) {
if (concept instanceof RecordConcept) {
return _getBoundWithOtherRecordConcept((RecordConcept) concept,
boundType);
} else {
Concept latticeEnd = null;
Concept otherLatticeEnd = null;
ConceptGraph conceptGraph = getOntology().getConceptGraph();
switch (boundType) {
case GREATESTLOWER:
latticeEnd = conceptGraph.bottom();
otherLatticeEnd = conceptGraph.top();
break;
case LEASTUPPER:
latticeEnd = conceptGraph.top();
otherLatticeEnd = conceptGraph.bottom();
break;
default:
throw new IllegalArgumentException("Unrecognized bound type: "
+ boundType + ". Expected either GREATESTLOWER or "
+ "LEASTUPPER");
}
if (concept.equals(otherLatticeEnd)) {
return this;
} else {
return latticeEnd;
}
}
}
/** Compute either the least upper bound or the greatest lower bound of
* this and another record concept.
*
* @param concept The other flat token infinite concept
* @param boundType Specifies the type of bound to be returned; either
* GREATESTLOWER or LEASTUPPER.
* @return The concept that is the bound of this and the given concept.
*/
private Concept _getBoundWithOtherRecordConcept(RecordConcept concept,
BoundType boundType) {
RecordConcept result = createRecordConcept(getOntology());
Set commonFields = _commonKeys(concept);
// The least upper bound is the record concept that only contains
// the common fields and the least upper bound of each concept in that
// field.
for (String field : commonFields) {
Concept fieldConcept = _getBoundFromConceptGraph(
this.getConcept(field), concept.getConcept(field),
boundType);
result.putConcept(field, fieldConcept);
}
// The greatest lower bound is a record concept that includes all the
// disjoint fields from both records in addition to the greatest lower
// bounds of each common field.
if (boundType.equals(BoundType.GREATESTLOWER)) {
Set disjointFields = new TreeSet(this.keySet());
disjointFields.removeAll(commonFields);
for (String field : disjointFields) {
result.putConcept(field, this.getConcept(field));
}
disjointFields = new TreeSet(concept.keySet());
disjointFields.removeAll(commonFields);
for (String field : disjointFields) {
result.putConcept(field, concept.getConcept(field));
}
}
return result;
}
///////////////////////////////////////////////////////////////////
//// protected constructors ////
/** Create a new Record concept, belonging to the given
* ontology.
*
* @param ontology The ontology to which this RecordConcept belongs.
* @exception NameDuplicationException Should never be thrown.
* @exception IllegalActionException If the base class throws it.
*/
protected RecordConcept(Ontology ontology) throws IllegalActionException,
NameDuplicationException {
super(ontology);
}
}