GEMS EMF Intelligence Tutorial with Mixed Constraints
In the previous tutorial (the GEMS EMF Intelligence Tutorial), we showed how GEMS EMF Intelligence can be used to associate OCL constraints with relationships between EObjects and then how GEMS EMF Intelligence can be used to derive valid (correct with respect to the associated constraints) endpoints for an EObject's relationships. In this tutorial, we show how constraints in multiple languages can be applied to a relationship and GEMS EMF Intelligence used to derive valid endpoints.
This tutorial uses the same library example from the GEMS EMF Intelligence Tutorial. We have a library of books but the books do not have the appropriate authors associated with them. We would like to be able to determine which books any given author wrote. In the previous example, we knew that each author only wrote books in a single category. We also knew that the same author wrote all the books in each category.
In this tutorial, we modify the original library example. Each author has encoded their name into the title by appending the Java hashcode of their name to the title of each of their books (yes...this is contrived...but it is just an example). This time, instead of determining all the books written by each author, we want to only know the books that they wrote that also became books on tape.
We would prefer not to encode all of these examples into OCL. Instead, we want to encode different parts of our understanding of the constraints into different languages. For the initial constraint that each author always writes books in the same category, we will reuse our OCL constraint from the last tutorial:
self.books->exists( b : Book | b.category->includes(target.category))
To ensure that only books that have an author's name encoded into them are associated with a 'Writer', we will use a constraint written in 'Groovy':
(see http://groovy.codehaus.org/ for information on the Groovy language)
In this Groovy constraint, the 'target' variable contains the proposed endpoint ('Book') for the author and the 'self' variable contains the author. All constraints in GEMS EMF Intelligence are between a source item (the 'self' variable) and an endpoint (the 'target' variable). You can always use 'self' and 'target' to refer to the source and target of the relationship.
Now, to ensure that we only get books that have also been made into books on tape, we will use a Prolog constraint:
(In order to get the Prolog part to work, you will need to download the GEMS Prolog Intelligence Plug-in from http://www.eclipse.org/gmt/gems/download.php and carefully follow its installation instructions that are available here: http://www.eclipse.org/gmt/gems/doc/prologintelligence-install.txt)
In this Prolog constraint, the 'Target' variable will be the proposed target. Although it is not shown in this constraint, the 'Self' variable can be used to refer to the source of the relationship. Please see the GEMS EMF Intelligence Prolog Plug-in Fact Format for information on how EObjects are converted into Prolog facts.
Know that we know the constraints that we are going to use, we can use GEMS EMF Intelligence to derive the 'Book's that have also become 'BooksOnTape' for a given 'Writer'. First, we construct the GEMS EMF Intelligence knowledge base:
//First, construct a basic EMFIntelligence knowledge base. //If you need to use constraints other than OCL, you can //install them by calling kb.getConstraintEvaluators().put(TYPE, ConstraintEvaluator) //This factory method will automatically load all ConstraintEvaluators //and ObjectTypeSystems that are provided as plugins to GEMS EMF Intelligence. EMFIntelligence kb = EMFIntelligenceFactory.createKB();
Next, we install the 'ConstraintEvaluators' that will be used to evaluate the constraints against the 'Books':
//If you this was run as an Eclipse plugin, the above line would //take care of this. However, since we can't guarantee that the //GroovyEvaluator or PrologEvaluator is installed, we just add them here to be safe. kb.getConstraintEvaluators().put(GroovyConstraint.TYPE, new GroovyEvaluator()); //Prolog has to have a reference to the KB PrologEvaluator peval = new PrologEvaluator(kb); //Prolog also gets added as a KB manager since it //has to incrementally synchronize its Prolog KB //with Refreh's KB kb.getKnowledgeManagers().add(peval); kb.getConstraintEvaluators().put(PrologConstraint.TYPE,peval);
Next, we construct a group of 'Books', a 'BookOnTape', and a 'Writer' for our example:
//Create a factory so that we can construct books and a writer EXTLibraryFactory fact = EXTLibraryPackage.eINSTANCE .getEXTLibraryFactory(); Book f1 = fact.createBook(); f1.setCategory(BookCategory.MYSTERY_LITERAL); Book f2 = fact.createBook(); f2.setCategory(BookCategory.BIOGRAPHY_LITERAL); Book f3 = fact.createBook(); f3.setCategory(BookCategory.MYSTERY_LITERAL); BookOnTape tape = fact.createBookOnTape(); //The writer that we are going to //query for valid books for Writer w1 = fact.createWriter(); w1.setFirstName("The Writer of"); w1.setLastName("Mystery"); w1.getBooks().add(f1); //Set the titles of our books //We add the writer's name's hashcode //to the first book. f1.setTitle("Mystery A "+w1.getName().hashCode()); f2.setTitle("Biography B"); f3.setTitle("Mystery C"); //We have only a single book that has become a //BookOnTape tape.setTitle("Mystery A "+w1.getName().hashCode());
After constructing the library, we add the various EObjects to the GEMS EMF Intelligence KB:
//Add the books to the knowledge base kb.add(f1); kb.add(f2); kb.add(f3); //Add the BookOnTape kb.add(tape); //Add the writer to the knowledge base kb.add(w1);
We also need to tell the KB what the constraints are on our 'books' relationship of an author. First, we create the OCL, Groovy, and Prolog constraints we described above:
OCLConstraint con1 = new OCLConstraint("self.books->exists( b : Book | b.category->includes(target.category))"); GroovyConstraint gcon = new GroovyConstraint("target.title.contains(\"\"+self.name.hashCode())"); PrologConstraint pcon = new PrologConstraint("self_title(Target,N),self_title(Tape,N),is_a(Tape,bookontape)");
Next, we have to associate these constraints with the 'books' EStructuralFeature of the 'Writer':
//This is the type that we are going to //be binding the constraint to. The //constraint will only apply to writers. EClass sourceType = w1.eClass(); //This is the feature (relationship) that //we will be binding the constraint to. EStructuralFeature booksFeature = sourceType.getEStructuralFeature("books"); //Post the constraints to the knowledge //base kb.add(sourceType, booksFeature, con1); //the OCL constraint kb.add(sourceType, booksFeature, gcon); //the Groovy constraint kb.add(sourceType, booksFeature, pcon); //the Prolog constraint
Now, all we need to do is ask GEMS EMF Intelligence to derive a set of books that meets our three constraints:
//Ask EMFIntelligence to derive the valid //values for the feature. This should only //return book f3. List valid = kb.validTargets(w1, booksFeature);
The variable 'valid' will contain the set of 'Books' that satisfies all three of our constraints.