GEMS EMF Intelligence Prolog Plug-in Fact Format

From Eclipsepedia

Jump to: navigation, search

The GEMS Prolog Intelligence plug-in allows you to run prolog queries against EMF models (and plain Java objects - discussed at the end of this page). As EObjects are asserted into a GEMS Intelligence knowledge base (KB), the GEMS Prolog Intelligence libraries create a Prolog representation of the EObjects.

Each EObject is assigned a unique id. The id is the concatenation of the string "id_" and the hashcode of the EObject. The basic decomposition of an EObject produces facts that assert properties associated with each id. For example:

self_name(id_1234, 'foo').

This fact would be produced by an EObject with hashcode '1234' and an EStructuralFeature called 'name' with value 'foo'.

The Prolog KB includes two different kinds of facts. First, there are "is_a" facts representing the types of an object. For a given EObject, an "is_a" fact is produced for the object's EClass and all super types of the object's EClass (obtained via EClass.getEAllSuperTypes()). For example, if we have an EClass 'BookOnTape' that extends 'AudioVisualItem' that extends 'CirculatingItem', the following facts would be produced:

is_a(id_1234, bookontape).
is_a(id_1234, audiovisualitem).
is_a(id_1234, circulatingitem).

The EClass names are converted to lower case to avoid potential conflicts with Prolog variables (anything that starts with an uppercase letter).

The second type of facts produced by GEMS Prolog Intelligence are facts about the EStructuralFeatures of an EObject. For each EStructuralFeature in the EObject, the KB includes a fact:

self_EStructuralFeatureName( id_XXXXX, EStructuralFeatureValue).

For an instance of the 'AudioVisualItem' EClass:

public interface AudioVisualItem extends CirculatingItem
{
   public String getTitle();
 
    ....

   public boolean getDamaged();
    ....
}

with id "id_1234," title "Moby Dick" and damaged "false," GEMS Prolog Intelligence would produce the facts:

self_title(id_1234, 'Moby Dick').
self_damaged(id_1234, false).

The combined facts for the EObject would be:

is_a(id_1234, audiovisualitem).
is_a(id_1234, circulatingitem).
self_title(id_1234, 'Moby Dick').
self_damaged(id_1234, false).


To write GEMS EMF Intelligence constraints against the KB, GEMS Intelligence provides two special variables: 'Self' and 'Target'. 'Self' is replaced with the id of the EObject that is the source of the relationship that you are querying to find endpoints for. In the following example, we add a simple Prolog constraint to an EStructuralFeature and then query for valid endpoints:

EMFIntelligence kb = ...;

EClass writerEClass = ...//get the Writer EClass
EStructuralFeature booksFeature = bookEClass.getEStructuralFeature("books");

PrologConstraint con = new PrologConstraint("self_name(Self, MyName), 
                                             self_name(Target, TargetName),
                                             MyName == TargetName").

kb.add(writerEClass, booksFeature, con);

Writer obj  = ...; //a Writer EObject with hashcode 1234
List valid = kb.validTargets(obj, booksFeature);

The Prolog constraint:

self_name(Self, MyName), 
self_name(Target, TargetName),
MyName == TargetName

is transformed into the Prolog query:

self_name(id_1234, MyName), 
self_name(Target, TargetName),
MyName == TargetName

To find the valid targets for the EStructuralFeature, GEMS Prolog Intelligence will issue the query:

findall(Target,
            (
             self_name(id_1234, MyName), 
             self_name(Target, TargetName),
             MyName == TargetName
             ),
         ValidTargets)

If you need to run Prolog queries against non-EMF objects, there are three important interfaces to implement:

public interface ObjectType {
	public String getName();
	public boolean instanceOf(Object obj);
	public List<ObjectType> getSuperTypes();
	public List<ObjectType> getDerivedTypes();
}

public interface ObjectView {
	public boolean supports(Object obj);
	public Map getView(Object obj);
}

public interface ObjectTypeSystem {
	
	public boolean supports(Object o);
	public ObjectView getObjectView(Object obj);
	public ObjectType getObjectType(Object obj);
}

The ObjectType is an extremely simple class for determining the type of an Object. Furthermore, the ObjectType can be queried for its super types and derived types. The instanceOf method returns true if the passed in object is an instance of the ObjectType. The ObjectType of an object is used to produce the "is_a" facts in the Prolog KB:

//simplified pseudo-code

ObjectType type = typeSystem.getObjectType(someObj);
prolog.assert("is_a", someObj, type.getName());

List<ObjectType> superTypes = type.getSuperTypes();
for(ObjectType stype : superTypes) {
  prolog.assert("is_a", someObj, stype.getName());
}

The ObjectView is a class that represents an object as a series a Map. Each key in the map represents a property of the object. For example, an ObjectView could be created that checks all Java Bean style get/set methods on an Object and for each getXXXX method, places the value in the map via Map.put("XXXX", obj.getXXXX()). For each key/value pair in the Map, Prolog Intelligence produces a "self_XXX" fact:

//simplified pseudo-code

Map view = objectView.getView(someObj);
for(Object key : view.keySet()){
  Object value = view.get(key);
  String stringrepresentation = toPrologString(value);
  prolog.assert("self_"+key, someObj, stringrepresentation);
}

The ObjectTypeSystem is used by GEMS Intelligence to obtain information on the types of objects and a view for observing the properties of an object. The ObjectTypeSystem is not absolutely required, you can directly call EMFIntelligence (or RefreshKB) installType(yourObjectType) and getObjectViews().add(yourObjectView). However, it generally makes sense to create an ObjectTypeSystem and install it via a call to getTypeSystems().add(yourTypeSystem). The 'supports(Object obj)' method should return true if the ObjectTypeSystem knows how to create ObjectViews and ObjectTypes for the specified object.

In some cases, you may need to customize the facts that are generated by GEMS Intelligence into the Prolog KB. First, let's look at how to change how an Object value for a property is converted into a String for Prolog:

//simplified pseudo-code

Map view = objectView.getView(someObj);
for(Object key : view.keySet()){
  Object value = view.get(key);

  //Here, we convert the value into a string to
  //create a prolog fact of the form:
  //"self_XXXX("+id+","+stringrepresentation+")."; 
  String stringrepresentation = toPrologString(value);
  ...
}

Let's say we want to convert Booleans to either 1 or 0 rather than true or false. To accomplish this, we need to create a 'org.refresh.prolog.ValueConverter':

public class OneOrZeroBooleanConverter implements org.refresh.prolog.ValueConverter {
     public String getValue(Object v){
        if(v instanceof Boolean){
           if(((Boolean)v).booleanValue()){
              return "1";
           }
           else {
              return "0";
           } 
        }
        else {
           return null; //if we don't know how to convert it, we return null
        }
     }
}

Now to tell GEMS Intelligence to use our converter:

PrologEvaluator peval = ...;
KnowledgeBase kb = peval.getKB();
BasicFactProducer fp = (BasicFactProducer)kb.getFactProducer();
fp.getValueConverters().add(new OneOrZeroBooleanConverter());

One key thing that we haven't touched on yet is how GEMS Intelligence converts relationships between objects in the knowledge base. Let's say we have the following class:

public class Foo {
  public Foo getParent(){...}
  public List<Foo> getFriends(){...}
}

Now, let's say we have two Foos: fooParent (id_000) and fooChild (id_123). If both of these objects are in the KB, then the decomposition into facts of fooChild will be:

is_a(id_123, foo).
self_parent(id_123, id_000).
...

If fooParent was not in the KB, the facts would be:

is_a(id_123, foo).
self_parent(id_123, 'fooParent.toString() result').
...

What is and isn't considered a part of the KB is determined by what is and isn't in the List returned by EMFIntelligence (or RefreshKB) 'getKnowledgeBase()'. If you have a group of ojbects that all have references to each other, you need to first add each on to this list and then call the 'add(...)' method on the EMFIntelligence or RefreshKB KB. Example:

EMFIntelligence kb = ...;
kb.getKnowledgeBase().add(fooParent);
kb.getKnowledgeBase().add(fooChild);
kb.add(fooParent);
kb.add(fooParent);

By first adding the objects to the list used to determine what is inside the KB, we guarantee that properties that reference other objects in the KB will be set to the id of the referenced object.

If you want to completely change how GEMS Prolog Intelligence represents objects in Prolog, you can implement the interface:

package org.refresh.prolog;

public interface FactProducer {
	public List<Fact> produceFacts(Object obj, Map props, List<ObjectType> types, List kb);
}

Each 'Fact' that your class produces is a simple representation of a Prolog fact:

//so the fact self_name(123, 'foo') would have
public interface Fact {

        //this would return 'self_name'
	public String getPredicate();

        //this would return ['123', 'foo']
	public Object[] getArguments();
}

The Map passed in via the 'props' variable contains the relevant key/value pairs for the object. The List passed via 'kb' contains the elements considered to be in the KB. Finally, the 'types' List contains all ObjectType's for which a call to ObjectType.instanceOf( obj ), would return true.

If you create your own FactProducer, to install it:

PrologEvaluator peval = ...;
KnowledgeBase kb = peval.getKB();
kb.setFactProducer(new MyFactProducer());