blob: 6dbeb2d2bac1bd5e59b371c022441c46c3e45331 [file] [log] [blame]
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Customizing the Environment</title>
<link href="book.css" rel="stylesheet" type="text/css">
<meta content="DocBook XSL Stylesheets V1.75.1" name="generator">
<link rel="home" href="index.html" title="OCL Documentation">
<link rel="up" href="ProgrammersGuide.html" title="Classic Ecore/UML Programmers Guide">
<link rel="prev" href="AbstractSyntax.html" title="OCL Abstract Syntax Model">
<link rel="next" href="Persistence.html" title="OCL Persistence">
</head>
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
<h1 xmlns:l="http://docbook.sourceforge.net/xmlns/l10n/1.0">Customizing the Environment</h1>
<div class="section" title="Customizing the Environment">
<div class="titlepage">
<div>
<div>
<h2 class="title" style="clear: both">
<a name="CustomizingtheEnvironment"></a>Customizing the Environment</h2>
</div>
</div>
</div>
<p>An application that integrates OCL may find it advantageous to provide its users with an
enhanced OCL environment, to simplify their task of formulating OCL constraints and queries.
For example, an application might define additional &ldquo;primitive&rdquo; operations on the OCL
standard data types that are pertinent to its domain, or &ldquo;global&rdquo; variables that inject
useful objects into the user&rsquo;s context. It is also possible to customize the way
&ldquo;hidden&rdquo; opposites are looked up and navigated, specifically to allow reverse navigation
across Ecore references that have no opposite defined.</p>
<div class="section" title="Defining Global Variables">
<div class="titlepage">
<div>
<div>
<h3 class="title">
<a name="DefiningGlobalVariables"></a>Defining Global Variables</h3>
</div>
</div>
</div>
<p>Consider an application that allows end-users to specify conditions, using OCL, to filter
the objects that are shown in the user interface. Given a sufficiently rich model
(expressed in Ecore or UML) of the objects that the UI presents, many conditions can be
expressed entirely in terms of this model. However, some queries might depend on state
of the application, itself, not the data: which perspective is active, whether some view
is showing, or even the time of day. These are not characteristics of the objects that the
user wishes to filter.</p>
<p>Such an application might, then, choose to define application-specific variables that a
filter condition can query:
<code class="code">app$perspective</code>,
<code class="code">app$views</code>,
<code class="code">app$time</code>. Or, perhaps a
single variable
<code class="code">app$</code>, that has properties that a condition can access:
</p>
<div class="literallayout">
<p>
<code class="code">--&nbsp;filter&nbsp;out&nbsp;OCL&nbsp;files&nbsp;in&nbsp;the&nbsp;Web&nbsp;Development&nbsp;perspective<br>
self.extension&nbsp;=&nbsp;'ocl'&nbsp;and&nbsp;app$.perspective&nbsp;=&nbsp;'Web&nbsp;Development'<br>
</code>
</p>
</div>
<p></p>
<p>To do this, we define a small Ecore model of our application context, e.g.:</p>
<p>
</p>
<div class="mediaobject">
<img src="images/5160-appcontext.png"></div>
<p>
</p>
<p>Then, in the code that parses a user&rsquo;s filter condition:</p>
<div class="literallayout">
<p>
<code class="code">OCL&lt;?,&nbsp;EClassifier,&nbsp;?,&nbsp;?,&nbsp;?,&nbsp;?,&nbsp;?,&nbsp;?,&nbsp;?,&nbsp;Constraint,&nbsp;EClass,&nbsp;EObject&gt;&nbsp;ocl;<br>
ocl&nbsp;=&nbsp;OCL.newInstance(EcoreEnvironmentFactory.INSTANCE);<br>
<br>
OCLHelper&lt;EClassifier,&nbsp;?,&nbsp;?,&nbsp;Constraint&gt;&nbsp;helper&nbsp;=&nbsp;ocl.createOCLHelper();<br>
helper.setContext(MyPackage.Literals.FILE);<br>
<br>
//&nbsp;create&nbsp;a&nbsp;variable&nbsp;declaring&nbsp;our&nbsp;global&nbsp;application&nbsp;context&nbsp;object<br>
Variable&lt;EClassifier,&nbsp;EParameter&gt;&nbsp;appContextVar&nbsp;=<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ExpressionsFactory.eINSTANCE.createVariable();<br>
appContextVar.setName("app$");<br>
appContextVar.setType(AppPackage.Literals.APP_CONTEXT);<br>
<br>
//&nbsp;add&nbsp;it&nbsp;to&nbsp;the&nbsp;global&nbsp;OCL&nbsp;environment<br>
ocl.getEnvironment().addElement(appContextVar.getName(),&nbsp;appContextVar,&nbsp;true);<br>
<br>
List&lt;Constraint&gt;&nbsp;conditions&nbsp;=&nbsp;new&nbsp;ArrayList&lt;Constraint&gt;();<br>
<br>
//&nbsp;parse&nbsp;the&nbsp;user's&nbsp;filter&nbsp;conditions<br>
for&nbsp;(String&nbsp;cond&nbsp;:&nbsp;getFilterConditions())&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;conditions.add(helper.createInvariant(cond));<br>
}<br>
<br>
//&nbsp;apply&nbsp;the&nbsp;filters<br>
applyFilters(conditions);<br>
</code>
</p>
</div>
<p></p>
<p>The body of our hypothetical
<code class="code">applyFilters()</code> method must bind this
context variable to a value. In this case, the value can be computed when we apply the
filters:
</p>
<div class="literallayout">
<p>
<code class="code">AppContext&nbsp;appContext&nbsp;=&nbsp;AppFactory.eINSTANCE.createAppContext();<br>
<br>
//&nbsp;hypothetical&nbsp;workbench&nbsp;utilities<br>
appContext.setPerspective(WorkbenchUtil.getCurrentPerspective());<br>
appContext.getViews().addAll(WorkbenchUtil.getOpenViewIDs());<br>
appContext.setTime(new&nbsp;Date());<br>
<br>
List&lt;Query&lt;EClassifier,&nbsp;EClass,&nbsp;EObject&gt;&gt;&nbsp;queries&nbsp;=<br>
&nbsp;&nbsp;&nbsp;&nbsp;new&nbsp;ArrayListlt;Query&lt;EClassifier,&nbsp;EClass,&nbsp;EObject&gt;&gt;(constraints.size());<br>
<br>
for&nbsp;(Constraint&nbsp;next&nbsp;:&nbsp;constraints)&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;Query&lt;EClassifier,&nbsp;EClass,&nbsp;EObject&gt;&nbsp;query&nbsp;=&nbsp;ocl.createQuery(next);<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;bind&nbsp;the&nbsp;variable&nbsp;value<br>
&nbsp;&nbsp;&nbsp;&nbsp;query.getEvaluationEnvironment().add("app$",&nbsp;appContext());<br>
&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;queries.add(query);<br>
}<br>
<br>
filter(queries);&nbsp;&nbsp;//&nbsp;applies&nbsp;these&nbsp;filters&nbsp;to&nbsp;the&nbsp;current&nbsp;objects<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;by&nbsp;evaluating&nbsp;the&nbsp;OCLS&nbsp;on&nbsp;them.<br>
</code>
</p>
</div>
<p></p>
</div>
<div class="section" title="Defining Helper Operations in Java">
<div class="titlepage">
<div>
<div>
<h3 class="title">
<a name="DefiningHelperOperationsinJava"></a>Defining Helper Operations in Java</h3>
</div>
</div>
</div>
<p>OCL allows the definition of additional operations and attributes using
<code class="code">def:</code> expressions. This is very convenient for the formulation of constraints, but what if the operation that we need is something like a regex pattern match?
</p>
<div class="literallayout">
<p>
<code class="code">class&nbsp;Person<br>
inv&nbsp;valid_ssn:&nbsp;self.ssn.regexMatch('\d{3}-\d{3}-\d{3}')&nbsp;&lt;&gt;&nbsp;null<br>
</code>
</p>
</div>
<p></p>
<p>We might try to define this using OCL, as an additional operation on the OCL Standard
Library&rsquo;s
<code class="code">String</code> primitive type:
</p>
<div class="literallayout">
<p>
<code class="code">class&nbsp;String<br>
def:&nbsp;regexMatch(pattern&nbsp;:&nbsp;String)&nbsp;:&nbsp;String&nbsp;=<br>
&nbsp;&nbsp;&nbsp;&nbsp;--&nbsp;???<br>
</code>
</p>
</div>
<p></p>
<p>The operations available in the OCL Standard Library simply are not sufficient to express
the value of this operation, which should return the substring matching a regex pattern or
<code class="code">null</code> if the pattern does not match. We need to implement this
operation in Java. We can do that by creating a custom
<code class="code">Environment</code>
that knows how to look up this operation, and an
<code class="code">EvaluationEnvironment</code>
that knows how it is implemented.
</p>
<p>First, let&rsquo;s start by defining a specialization of the
<a class="ulink" href="http://download.eclipse.org/ocl/javadoc/6.3.0/org/eclipse/ocl/ecore/EcoreEnvironment.html" target="_new">
<code class="code">EcoreEnvironment</code>
</a> . The constructor that is used to initialize the root environment of an
<code class="code">OCL</code>
instance will add our
<code class="code">regexMatch</code> additional operation to the
<code class="code">String</code> primitive type. The constructor that is used to create
nested environments copies the operation from its parent.
</p>
<div class="literallayout">
<p>
<code class="code">class&nbsp;MyEnvironment&nbsp;extends&nbsp;EcoreEnvironment&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;EOperation&nbsp;regexMatch;<br>
&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;this&nbsp;constructor&nbsp;is&nbsp;used&nbsp;to&nbsp;initialize&nbsp;the&nbsp;root&nbsp;environment<br>
&nbsp;&nbsp;&nbsp;&nbsp;MyEnvironment(EPackage.Registry&nbsp;registry)&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super(registry);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;defineCustomOperations();<br>
&nbsp;&nbsp;&nbsp;&nbsp;}<br>
&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;this&nbsp;constructor&nbsp;is&nbsp;used&nbsp;to&nbsp;initialize&nbsp;child&nbsp;environments<br>
&nbsp;&nbsp;&nbsp;&nbsp;MyEnvironment(MyEnvironment&nbsp;parent)&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super(parent);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;get&nbsp;the&nbsp;parent's&nbsp;custom&nbsp;operations<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;regexMatch&nbsp;=&nbsp;parent.regexMatch;<br>
&nbsp;&nbsp;&nbsp;&nbsp;}<br>
<br>
//&nbsp;override&nbsp;this&nbsp;to&nbsp;provide&nbsp;visibility&nbsp;of&nbsp;the&nbsp;inherited&nbsp;protected&nbsp;method<br>
&nbsp;&nbsp;&nbsp;&nbsp;@Override<br>
&nbsp;&nbsp;&nbsp;&nbsp;protected&nbsp;void&nbsp;setFactory(<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EnvironmentFactory&lt;EPackage,&nbsp;EClassifier,&nbsp;EOperation,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EStructuralFeature,&nbsp;EEnumLiteral,&nbsp;EParameter,&nbsp;EObject,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CallOperationAction,&nbsp;SendSignalAction,&nbsp;Constraint,&nbsp;EClass,&nbsp;EObject&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;factory)&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super.setFactory(factory);<br>
&nbsp;&nbsp;&nbsp;&nbsp;}<br>
&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;use&nbsp;the&nbsp;AbstractEnvironment's&nbsp;mechanism&nbsp;for&nbsp;defining<br>
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;"additional&nbsp;operations"&nbsp;to&nbsp;add&nbsp;our&nbsp;custom&nbsp;operation&nbsp;to<br>
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;OCL's&nbsp;String&nbsp;primitive&nbsp;type<br>
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;void&nbsp;defineCustomOperations()&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;pattern-matching&nbsp;operation<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;regexMatch&nbsp;=&nbsp;EcoreFactory.eINSTANCE.createEOperation();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;regexMatch.setName("regexMatch");<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;regexMatch.setEType(getOCLStandardLibrary().getString());<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EParameter&nbsp;parm&nbsp;=&nbsp;EcoreFactory.eINSTANCE.createEParameter();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parm.setName("pattern");<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parm.setEType(getOCLStandardLibrary().getString());<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;regexMatch.getEParameters().add(parm);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;annotate&nbsp;it&nbsp;so&nbsp;that&nbsp;we&nbsp;will&nbsp;recognize&nbsp;it<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;in&nbsp;the&nbsp;evaluation&nbsp;environment<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EAnnotation&nbsp;annotation&nbsp;=&nbsp;EcoreFactory.eINSTANCE.createEAnnotation();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;annotation.setSource("MyEnvironment");<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;regexMatch.getEAnnotations().add(annotation);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;define&nbsp;it&nbsp;as&nbsp;an&nbsp;additional&nbsp;operation&nbsp;on&nbsp;OCL&nbsp;String<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addOperation(getOCLStandardLibrary().getString(),&nbsp;regexMatch);<br>
&nbsp;&nbsp;&nbsp;&nbsp;}<br>
}<br>
</code>
</p>
</div>
<p></p>
<p>Next, we will define the corresponding specialization of the
<a class="ulink" href="http://download.eclipse.org/ocl/javadoc/6.3.0/org/eclipse/ocl/ecore/EcoreEvaluationEnvironment.html" target="_new">
<code class="code">EcoreEvaluationEnvironment</code>
</a> that will know how to evaluate calls to this custom operation:
</p>
<div class="literallayout">
<p>
<code class="code">class&nbsp;MyEvaluationEnvironment&nbsp;extends&nbsp;EcoreEvaluationEnvironment&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;MyEvaluationEnvironment()&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super();<br>
&nbsp;&nbsp;&nbsp;&nbsp;}<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;MyEvaluationEnvironment(<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EvaluationEnvironment&lt;EClassifier,&nbsp;EOperation,&nbsp;EStructuralFeature,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EClass,&nbsp;EObject&gt;&nbsp;parent)&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super(parent);<br>
&nbsp;&nbsp;&nbsp;&nbsp;}<br>
&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;Object&nbsp;callOperation(EOperation&nbsp;operation,&nbsp;int&nbsp;opcode,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;source,&nbsp;Object[]&nbsp;args)&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(operation.getEAnnotation("MyEnvironment")&nbsp;==&nbsp;null)&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;not&nbsp;our&nbsp;custom&nbsp;regex&nbsp;operation<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;super.callOperation(operation,&nbsp;opcode,&nbsp;source,&nbsp;args);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;("regexMatch".equals(operation.getName()))&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Pattern&nbsp;pattern&nbsp;=&nbsp;Pattern.compile((String)&nbsp;args[0]);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Matcher&nbsp;matcher&nbsp;=&nbsp;pattern.matcher((String)&nbsp;source);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;matcher.matches()?&nbsp;matcher.group()&nbsp;:&nbsp;null;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;new&nbsp;UnsupportedOperationException();&nbsp;&nbsp;//&nbsp;unknown&nbsp;operation<br>
&nbsp;&nbsp;&nbsp;&nbsp;}<br>
}<br>
</code>
</p>
</div>
<p></p>
<p>Finally, we define a specialization of the
<a class="ulink" href="http://download.eclipse.org/ocl/javadoc/6.3.0/org/eclipse/ocl/ecore/EcoreEnvironmentFactory.html" target="_new">
<code class="code">EcoreEnvironmentFactory</code>
</a> that creates our custom environments:
</p>
<div class="literallayout">
<p>
<code class="code">class&nbsp;MyEnvironmentFactory&nbsp;extends&nbsp;EcoreEnvironmentFactory&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;Environment&lt;EPackage,&nbsp;EClassifier,&nbsp;EOperation,&nbsp;EStructuralFeature,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EEnumLiteral,&nbsp;EParameter,&nbsp;EObject,&nbsp;CallOperationAction,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SendSignalAction,&nbsp;Constraint,&nbsp;EClass,&nbsp;EObject&gt;&nbsp;createEnvironment()&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MyEnvironment&nbsp;result&nbsp;=&nbsp;new&nbsp;MyEnvironment(getEPackageRegistry());<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result.setFactory(this);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;result;<br>
&nbsp;&nbsp;&nbsp;&nbsp;}<br>
&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;Environment&lt;EPackage,&nbsp;EClassifier,&nbsp;EOperation,&nbsp;EStructuralFeature,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EEnumLiteral,&nbsp;EParameter,&nbsp;EObject,&nbsp;CallOperationAction,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SendSignalAction,&nbsp;Constraint,&nbsp;EClass,&nbsp;EObject&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;createEnvironment(Environment&lt;EPackage,&nbsp;EClassifier,&nbsp;EOperation,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EStructuralFeature,&nbsp;EEnumLiteral,&nbsp;EParameter,&nbsp;EObject,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CallOperationAction,&nbsp;SendSignalAction,&nbsp;Constraint,&nbsp;EClass,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EObject&gt;&nbsp;parent)&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(!(parent&nbsp;instanceof&nbsp;MyEnvironment))&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;new&nbsp;IllegalArgumentException(<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Parent&nbsp;environment&nbsp;must&nbsp;be&nbsp;my&nbsp;environment:&nbsp;"&nbsp;+&nbsp;parent);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MyEnvironment&nbsp;result&nbsp;=&nbsp;new&nbsp;MyEnvironment((MyEnvironment)&nbsp;parent);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result.setFactory(this);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;result;<br>
&nbsp;&nbsp;&nbsp;&nbsp;}<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;EvaluationEnvironment&lt;EClassifier,&nbsp;EOperation,&nbsp;EStructuralFeature,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EClass,&nbsp;EObject&gt;&nbsp;createEvaluationEnvironment()&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;new&nbsp;MyEvaluationEnvironment();<br>
&nbsp;&nbsp;&nbsp;&nbsp;}<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;EvaluationEnvironment&lt;EClassifier,&nbsp;EOperation,&nbsp;EStructuralFeature,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EClass,&nbsp;EObject&gt;&nbsp;createEvaluationEnvironment(<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EvaluationEnvironment&lt;EClassifier,&nbsp;EOperation,&nbsp;EStructuralFeature,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EClass,&nbsp;EObject&gt;&nbsp;parent)&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;new&nbsp;MyEvaluationEnvironment(parent);<br>
&nbsp;&nbsp;&nbsp;&nbsp;}<br>
}<br>
</code>
</p>
</div>
<p></p>
<p>Now, we can use our environment to parse the kind of expression that we were looking for:</p>
<div class="literallayout">
<p>
<code class="code">OCL&lt;?,&nbsp;EClassifier,&nbsp;?,&nbsp;?,&nbsp;?,&nbsp;?,&nbsp;?,&nbsp;?,&nbsp;?,&nbsp;Constraint,&nbsp;EClass,&nbsp;EObject&gt;&nbsp;ocl;<br>
ocl&nbsp;=&nbsp;OCL.newInstance(new&nbsp;MyEnvironmentFactory());<br>
<br>
OCLHelper&lt;EClassifier,&nbsp;?,&nbsp;?,&nbsp;Constraint&gt;&nbsp;helper&nbsp;=&nbsp;ocl.createOCLHelper();<br>
helper.setContext(MyPackage.Literals.PERSON);<br>
<br>
//&nbsp;double&nbsp;the&nbsp;'\'&nbsp;to&nbsp;escape&nbsp;it&nbsp;in&nbsp;a&nbsp;Java&nbsp;string&nbsp;literal<br>
Constraint&nbsp;validSSN&nbsp;=&nbsp;helper.createInvariant(<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"self.ssn.regexMatch('\\d{3}-\\d{3}-\\d{3}')&nbsp;&lt;&gt;&nbsp;null");<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
Person&nbsp;person&nbsp;=&nbsp;getPersonToValidate();<br>
<br>
System.out.printf("%s&nbsp;valid&nbsp;SSN:&nbsp;%b%n",&nbsp;person,&nbsp;ocl.check(person,&nbsp;validSSN));<br>
</code>
</p>
</div>
<p></p>
</div>
<div class="section" title="Selecting a Package Lookup Strategy">
<div class="titlepage">
<div>
<div>
<h3 class="title">
<a name="SelectingaPackageLookupStrategy"></a>Selecting a Package Lookup Strategy</h3>
</div>
</div>
</div>
<p>When package names are provided in OCL expressions, e.g., when representing types in an
<code class="code">oclIsKindOf</code> call, these names are looked up using a specific
strategy. By default, the lookup proceeds starting at the parsing context, traversing
up the package hierarchy. If the package name cannot be resolved this way, for the Ecore
binding a lookup is performed in the
<code class="code">EPackage.Registry</code>. By
default, the package name provided is compared to the names of the packages that are
contained as values in the registry.
</p>
<p>In rare cases there may be ambiguous package names. For example, if an OCL expression
is to be parsed using a classifier from the OCL AST metamodel as its context, the
context package is
<code class="code">ocl::ecore</code>. If such an expression is
trying to reference a type from the EMF Ecore package with package name
<code class="code">ecore</code>, the EMF Ecore package is hidden by the lookup
happening relative to the context package. Instead of the EMF Ecore package, the
<code class="code">ocl::ecore</code> package will be found.
</p>
<p>Such an ambiguity can be resolved by using a dedicated
<code class="code">EPackage.Registry</code>
which registers the otherwise ambiguous packages with a special &ldquo;URI&rdquo; that represents a
simple alias name for the package. In order to force the OCL parser to look up packages
by those alias names, an option needs to be set on the OCL environment, as follows:
</p>
<div class="literallayout">
<p>
<code class="code">&nbsp;&nbsp;&nbsp;&nbsp;Registry&nbsp;r&nbsp;=&nbsp;new&nbsp;EPackageRegistryImpl();<br>
&nbsp;&nbsp;&nbsp;&nbsp;r.putAll(EPackage.Registry.INSTANCE);<br>
&nbsp;&nbsp;&nbsp;&nbsp;r.put("EMFEcore",&nbsp;EcorePackage.eINSTANCE);<br>
&nbsp;&nbsp;&nbsp;&nbsp;r.put("OCLEcore",&nbsp;org.eclipse.ocl.ecore.EcorePackage.eINSTANCE);<br>
&nbsp;&nbsp;&nbsp;&nbsp;OCL&nbsp;ocl&nbsp;=&nbsp;OCL.newInstance(new&nbsp;EcoreEnvironmentFactory(r));<br>
&nbsp;&nbsp;&nbsp;&nbsp;((EcoreEnvironment)&nbsp;ocl.getEnvironment()).setOption(<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ParsingOptions.PACKAGE_LOOKUP_STRATEGY,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ParsingOptions.PACKAGE_LOOKUP_STRATEGIES.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LOOKUP_PACKAGE_BY_ALIAS_THEN_NAME);<br>
&nbsp;&nbsp;&nbsp;&nbsp;Helper&nbsp;helper&nbsp;=&nbsp;ocl.createOCLHelper();<br>
&nbsp;&nbsp;&nbsp;&nbsp;helper.setContext(<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;org.eclipse.ocl.ecore.EcorePackage.eINSTANCE.getOCLExpression());<br>
&nbsp;&nbsp;&nbsp;&nbsp;org.eclipse.ocl.ecore.OCLExpression&nbsp;expr&nbsp;=&nbsp;helper.createQuery(<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"self.oclIsKindOf(EMFEcore::EClassifier)&nbsp;and&nbsp;not<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.oclIsKindOf(OCLEcore::OCLExpression)");<br>
</code>
</p>
</div>
<p></p>
<p>In the example above, two packages with ambiguous simple names (EMF Ecore package and
OCL Ecore package, both with simple name
<code class="code">ecore</code>) are added with
alias names
<code class="code">EMFEcore</code> and
<code class="code">OCLEcore</code>,
respectively. The package lookup strategy is then set to
<code class="code">LOOKUP_PACKAGE_BY_ALIAS_THEN_NAME</code> which allows OCL expressions to reference
the packages by their aliases, as in
<code class="code">self.oclIsKindOf(EMFEcore::EClassifier) and not self.oclIsKindOf(OCLEcore::OCLExpression)</code>.
</p>
<p>Note, that the use of a delegating registry (constructor
<code class="code">EPackageRegistryImpl(EPackage.Registry)</code>) does not work
because a registry initialized this way does not forward the call to
<code class="code">values()</code> which would be required by the OCL
package lookup implementation. Instead, if the packages registered with the
default registry are required, they need to be copied to a new registry
using
<code class="code">putAll</code> as shown above.
</p>
</div>
<div class="section" title="Customizing Hidden Opposite Lookup and Navigation">
<div class="titlepage">
<div>
<div>
<h3 class="title">
<a name="CustomizingHiddenOppositeLookupandNavigation"></a>Customizing Hidden Opposite Lookup and Navigation</h3>
</div>
</div>
</div>
<p>The default
<a class="ulink" href="http://download.eclipse.org/ocl/javadoc/6.3.0/org/eclipse/ocl/ecore/EcoreEnvironmentFactory.html" target="_new">
<code class="code">EcoreEnvironmentFactory</code>
</a> produces environments which can find references that have an annotation with source
<code class="code">http://schema.omg.org/spec/MOF/2.0/emof.xml</code>
that have a detail with key
<code class="code">Property.oppositeRoleName</code>. In the class that is the type of the reference,
and all its subclasses, for OCL this annotation defines an otherwise &ldquo;hidden&rdquo; opposite property which can be used
in OCL expressions. This can be convenient when it is not possible or desirable to define an explicit
opposite reference, e.g., because the class that would have to own the opposite reference can&rsquo;t easily be
modified or the serialization of that class must not be changed.
</p>
<p>The logic used to find these &ldquo;hidden&rdquo; opposites and to navigate them is provided by implementations
of the
<a class="ulink" href="http://download.eclipse.org/ocl/javadoc/6.3.0/org/eclipse/ocl/ecore/OppositeEndFinder.html" target="_new">
<code class="code">OppositeEndFinder</code>
</a> interface. By default, the
<code class="code">EcoreEnvironmentFactory</code> uses the
<a class="ulink" href="http://download.eclipse.org/ocl/javadoc/6.3.0/org/eclipse/ocl/ecore/DefaultOppositeEndFinder.html" target="_new">
<code class="code">DefaultOppositeEndFinder</code>
</a> implementation. It performs the lookup of annotated references by maintaining a cache based on
the Ecore package registry. Successful navigation of those &ldquo;hidden&rdquo; opposites requires an
<a class="ulink" href="http://download.eclipse.org/modeling/emf/emf/javadoc/2.6.0/org/eclipse/emf/ecore/util/ECrossReferenceAdapter.html" target="_new">
<code class="code">ECrossReferenceAdapter</code>
</a> to be registered for the containment hierarchy or the resource or resource set that should be used as the scope of the navigation.
</p>
<p>Obviously,
<a class="ulink" href="http://download.eclipse.org/modeling/emf/emf/javadoc/2.6.0/org/eclipse/emf/ecore/util/ECrossReferenceAdapter.html" target="_new">
<code class="code">ECrossReferenceAdapter</code>
</a> has a significant downside: it responds to &ldquo;hidden&rdquo; opposite navigation requests only based on what has so far been loaded by EMF. If the set of resources held by an underlying EMF storage system contains more resources than have so far been loaded into the resource set, non-loaded content from that storage system won&rsquo;t be considered by the
<code class="code">ECrossReferenceAdapter</code>. Given a store with reasonable search capabilities it is desirable to take advantage of these capabilities also to perform reverse navigation of those &ldquo;hidden&rdquo; opposites. To achieve this, a specific implementation of the
<a class="ulink" href="http://download.eclipse.org/ocl/javadoc/6.3.0/org/eclipse/ocl/ecore/OppositeEndFinder.html" target="_new">
<code class="code">OppositeEndFinder</code>
</a> interface can be provided. It may be a specialization of
<a class="ulink" href="http://download.eclipse.org/ocl/javadoc/6.3.0/org/eclipse/ocl/ecore/DefaultOppositeEndFinder.html" target="_new">
<code class="code">DefaultOppositeEndFinder</code>
</a>, e.g., when the reference lookup based on the Ecore package registry is sufficient and only the navigation behavior shall be redefined:
</p>
<div class="literallayout">
<p>
<code class="code">class&nbsp;MyOppositeEndFinder&nbsp;extends&nbsp;DefaultOppositeEndFinder&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;MyOppositeEndFinder(EPackage.Registry&nbsp;registry)&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super(registry);<br>
&nbsp;&nbsp;&nbsp;&nbsp;}<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;@Override<br>
&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;Object&nbsp;navigateOppositeProperty(EStructuralFeature&nbsp;property,&nbsp;Object&nbsp;target)&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Collection&lt;Object&gt;&nbsp;result&nbsp;=&nbsp;null;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EObject&nbsp;eTarget&nbsp;=&nbsp;(EObject)&nbsp;target;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;do&nbsp;something&nbsp;clever,&nbsp;e.g.,&nbsp;using&nbsp;your&nbsp;underlying&nbsp;store's&nbsp;query&nbsp;facility&nbsp;or<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;the&nbsp;new&nbsp;EMF&nbsp;Query2&nbsp;component&nbsp;(incubation)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;...<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;result;<br>
&nbsp;&nbsp;&nbsp;&nbsp;}<br>
}<br>
</code>
</p>
</div>
<p></p>
<p>With this, OCL can be instantiated using the custom opposite end finder as follows:</p>
<div class="literallayout">
<p>
<code class="code">&nbsp;&nbsp;OCL&nbsp;ocl&nbsp;=&nbsp;OCL.newInstance(new&nbsp;EcoreEnvironmentFactoryWithHiddenOpposites(<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EPackage.Registry.INSTANCE,&nbsp;new&nbsp;MyOppositeEndFinder()));<br>
&nbsp;&nbsp;...<br>
</code>
</p>
</div>
<p></p>
<p>With this, when the use of a property in an OCL expression cannot be resolved to an attribute
or reference, the opposite end finder is asked to look for a correspondingly-named "hidden"
opposite. Navigation across this &ldquo;hidden&rdquo; opposite will then call the
<code class="code">navigateOppositeProperty</code> method on
<code class="code">MyOppositeEndFinder</code>.
</p>
</div>
</div>
</body>
</html>