# Tutorial: EMF Model Query Statements

## Contents

- Overview
- References
- Introduction
- Constructing a Query Statement
- Providing the Query Clauses
- Applying Conditions
- Nesting Conditions
- Executing the Query
- Additional Examples
- Summary

## Overview

The EMF Model Query framework provides a set of tools to construct and execute query statements. These query statements provide a client with a uniform way of discovering and potentially modifying the matching EObjects. Queries are first constructed with their query clauses and then they are ready to be executed.

[back to top]

## References

This tutorial assumes that the reader is familiar with EMF and is familiar with the concept of querying data. A crucial part of understanding EMF is being able to understand its reflective mechanisms including `EClasses` and `EStructuralFeatures`.

For reference, the full example for this tutorial is available.

[back to top]

## Introduction

In order to demonstrate EMF Query, we will be making use of the extended library model (EXTLibrary). This model is a variant of the standard EMF example model used in many of its tutorials.

For those readers who are not familiar with this model, it describes a library with books and writers. The most important aspect of the library model for this tutorial is the fact that books are modeled as EObjects whose EClass is `Book` and they contain an EStructuralFeature called `pages` that stores an integer number of pages contained in the book.

The goal of this tutorial is to create an EMF query that will discover which books contain more than 500 pages. These books are considered "large" books.

[back to top]

## Constructing a Query Statement

There are two query statements available: SELECT and UPDATE. The SELECT statement provides querying without modification while the UPDATE statement provides querying with modification. In this case, we require only querying without the modification.

Often times, pseudocode can be used to clarify the function of the query statement. In EMF query the pseudocode is very close to the code. We will use pseudocode for now and switch back to the actual code near the end of this tutorial.

Here is our query so far:

    SELECT
        FROM [source]
        WHERE [condition]

[back to top]

## Providing the Query Clauses

Every query statement requires some query clauses. The SELECT statement requires two clauses, a "FROM" and a "WHERE." The former clause describes the source of EObjects where SELECT can iterate in order to derive results. The latter clause describes the criteria for an EObject that matches.

The FROM clause requires an EObjectSource. We will trivially satisfy the FROM clause by providing a collection of EObjects called `selectedEObjects`:

    SELECT
        FROM selectedEObjects
        WHERE [condition]

The FROM clause defaults to hierarchical iteration, which means that for each EObject in the selectedEObjects collection, the SELECT statement will traverse its contained EObjects (`eContents()`) recursively until it reaches the leaves of the containment subtree to find its matching EObjects.

[back to top]

## Applying Conditions

The final part of a SELECT statement is the WHERE clause along with its condition. This condition will be evaluated at each EObject encountered by the FROM clause to determine whether the EObject matches the criteria of the query. The condition provided to the WHERE clause falls under a specialized condition called an EObjectCondition that is a condition that is specially designed to evaluate an EObject.

Our original purpose for this query is to find book EObjects whose pages are larger than 500. The pages EStructuralFeature is an EAttribute whose value will be an integer so we will choose the EObjectAttributeValueCondition. Its purpose is to evaluate the value of a specific EAttribute:

    SELECT
        FROM selectedEObjects
        WHERE EObjectAttributeValueCondition
                    EXTLibraryPackage.eINSTANCE.getBook_Pages()
                    [inner condition]

[back to top]

## Nesting Conditions

Some conditions will require other conditions in order to perform their function. This gives clients enough versatility to formulate their queries.

In the case of EObjectAttributeValueCondition, it must be constructed with a Condition. Unlike the WHERE clause, it does not require the special EObjectCondition. This is because EAttributes may store primitive values like Strings, Integers or Booleans. Our EAttribute "pages" is an Integer EAttribute so we will use a number condition that will match a range of numerical values:

    SELECT
        FROM selectedEObjects
        WHERE EObjectAttributeValueCondition
                    EXTLibraryPackage.eINSTANCE.getBook_Pages()
                    NumberCondition.IntegerValue(500, MAX_VALUE)

Now we have the final pseudo-code representation of the query. The NumberCondition.IntegerValue condition will match any Integer between 500 and the maximum integer value inclusive.

[back to top]

## Executing the Query

Since the beginning of the tutorial, we have been operating in pseudocode. When we translate the pseudocode into EMF Query code we get the following:

    statement =
        new SELECT(
            new FROM(selectedEObjects),
            new WHERE(new EObjectAttributeValueCondition(
                        EXTLibraryPackage.eINSTANCE.getBook_Pages(),
                        new NumberCondition.IntegerValue(new Integer(500), new Integer(Integer.MAX_VALUE)))
            )
        )

The EStructuralFeatureValueGetter object is explicitly provided to perform the reflective retrieval of the structural feature value. This object may be substituted in order to provide more a more optimal way to retrieve this value. In the above example the default value getter is used although there are other constructors to allow clients to provide their own.

Every query statement has an `execute()` method, which returns back the collection of matching EObjects.

    statement.execute();

For robustness, the executor of the query statement should call the getException() on the returned IQueryResult of the execute() method in order to verify that no exceptions occurred during the execution of the query.

[back to top]

## Additional Examples

    BookCategory category;

    /*
     * Looking for writers whose authored books of the specified category
     */
    EObjectCondition condition = new EObjectReferenceValueCondition(
        new EObjectTypeRelationCondition(EXTLibraryPackage.eINSTANCE
            .getWriter()), EXTLibraryPackage.eINSTANCE.getWriter_Books(),
        new EObjectAttributeValueCondition(EXTLibraryPackage.eINSTANCE
            .getBook_Category(), new ObjectInstanceCondition(category)));

    // Build the query statement
    SELECT statement = new SELECT(
        new FROM(selectedEObjects),
        new WHERE(condition)
    );

    // Execute query
    return statement.execute();

The above query makes use of the EObjectReferenceValueCondition and EObjectTypeRelationCondition. The former allows one to evaluate the value of an EReference. In this case, it is evaluating the value of the books EReference. By default, if the EReference has a multiplicity larger than 1 this condition will default to a "ConditionPolicy.ANY," which means that it will match an EObject if any of its referenced EObjects matches the provided value condition.

Two nested conditions are provided to the EObjectReferenceValueCondition: a context condition and a value condition. The context condition evaluates against the container of the EReference while the value condition evaluates against the referenced EObjects. Notice that both conditions will have to be EObjectConditions because they will be matching against EObjects.

In the above query, the context condition is an EObjectTypeRelationCondition, which will ensure that the EObject has a certain EClass (type). The value condition was chosen to be the EObjectAttributeValueCondition, which will compare the book's category identity against the chosen category enumeration literal.

    Writer chosenWriter;
    String name = chosenWriter.getName();

    /*
     * Looking for books whose writer name is the specified name
     */
    EObjectCondition condition = new EObjectReferenceValueCondition(
        new EObjectTypeRelationCondition(EXTLibraryPackage.eINSTANCE.getBook()),
        EXTLibraryPackage.eINSTANCE.getBook_Author(),
        new EObjectAttributeValueCondition(EXTLibraryPackage.eINSTANCE
            .getWriter_Name(), new StringValue(name)));

    // Build the select query statement
    SELECT statement = new SELECT(
        new FROM(chosenWriter.eResource().getContents()),
        new WHERE(condition));

This query is similar in structure to the previous example. The differences are that the context condition is checking that the container of the EReference is a book and the author of the book has a value `name` for the name EAttribute.

[back to top]

## Summary

In this tutorial, we did the following:

1. Developed a query statement in pseudocode.
2. Satisfied the query statement and its clauses.
3. Used a nested condition to evaluate the value of an EAttribute.
4. Executed the query to produce the results.
5. Checked to ensure that the query did not generate any exceptions during its execution.
6. Analyzed some more sophistocated queries.

[back to top]

Copyright (c) 2000, 2007 IBM Corporation and others. All Rights Reserved.