Probably the fastest way to get started on writing a Translation-Library is to take a look at the contents of an existing one. Download and take a look at the contents of the Query Translation-Library. In addition, there are sections below which take you through the process step-by-step of generating a Translation-Library structure and then adding the translation capabilities to the generated Translation-Library.
Make sure you have the maven-ocltf-plugin installed in your $MAVEN_HOME/plugins directory. First make sure your maven.repo.central property includes the OCLTF maven repository: http://ocltf.sourceforge.net/ocltf/maven:
maven.repo.remote = http://www.ibiblio.org/maven/,http://ocltf.sourceforge.net/maven/
Then type the following to download and install it:
maven plugin:download -DgroupId=ocltf -DartifactId=maven-ocltf-plugin -Dversion=1.0-SNAPSHOT
Next we need to create the Translation-Library directory that will contain all required resources (you'll be editing these as you develop). We'll call our sample Translation-Library 'constraint'. This is simple with the maven-ocltf-plugin. After changing to the directory where you want the 'constraint' Translation-Library to be generated, just type the following:
maven ocltf:generate-translation-library -Dlibrary=constraint -Dtranslations=Oracle-SQL
After you've successfully generated your Translation-Library development directory. Change to that directory and take a look at what has been generated (the following table shows what you'll find):
cd constraint
Resource | Description |
---|---|
META-INF/translation-library.xml | The Translation-Library descriptor file. Contains the Translator(s) and Translation files supported by this Translation-Library. Take a look at the schema below for more detail. |
project.xml | The Maven POM for the constraint Translation-Library. This will allow you to build it after its been generated. |
translations/constraint/Oracle-SQL.vsl |
The translation Velocity template file.
This allows you to map fragments for Oracle-SQL to OCL fragments which are being parsed. In
addition to a Velocity template file, it's also an XML document. Take a look at
this schema for more detail.
|
src/java/org/ocltf/translation/constraint/ConstraintTranslator.java |
This is the java Translator
class mapped within the Translation-Library descriptor: translation-library.xml
|
src/java/org/ocltf/translation/constraint/ConstraintTranslatorException.java | This is the java exception class extending the TranslatorException. For any exception thrown during processing of the ConstraintTranslator, the exception SHOULD be an instance of this class. |
test/translations/constraint/TranslationTest-Oracle-SQL.xml |
This is the XML file that allows you to easily test your Translator/Translation
during development of a Translation-Library. There should be one
Translator-Test-*.xml file per translation. Take a look at its
schema for more detail.
|
Ok, since you now are within your 'constraint' directory, you can run the
ocltf:test-translation
goal to make sure everything was setup correctly:
maven ocltf:test-translation
From the previous step, you should have seen some information printed
to the screen and at the end it should say BUILD SUCCESSFUL
. If it failed
for some reason, then contact me and let me know, because obviously something isn't right.
If all the previous steps have succeeded, you're ready to start developing, congradulations!
Go on to the next section
Adding Translation Capabilities to your Translation-Library.
Now that you've created an empty Translation-Library from the
previous section. Its time to start developing your library.
The first thing you'll want to do, is add an expression to the body of the
<from/>
element of your translation test file
TranslationTest-Oracle-SQL.xml. Since our example is a
'constraint'
Translation-Library, I'll add the expression representing a constraint
that expresses "within all LegalAgreements, documentTitle must be unique".
<expression contextType="org.ocltf.contracts.LegalAgreement"> <from> context LegalAgreement inv: allInstances -> isUnique(documentTitle) </from> <to> </to> </expression>
We'll now execute the ocltf:test-translation goal once again but this time we'll add the '-Dtrace=true' property. Since we've added an expression to the <from/> element in the previous step, this will allow us to see, in what order and how the OCL parser parses the expression that we added. This is necessary since we'll need to know what methods to override in our ConstraintTranslator class to perform the translation of the expression.
maven ocltf:test-translation -Dtrace=true
Your output should look like the following:
[junit] 13:36:49,795 INFO [TraceTranslator] ======================== Tracing Expression ======================== [junit] 13:36:49,795 INFO [TraceTranslator] context LegalAgreement inv: allInstances -> isUnique(documentTitle) [junit] 13:36:49,805 INFO [TraceTranslator] ======================== ================== ======================== [junit] 13:36:50,216 WARN [Fragment] WARNING! No name has been given to a 'kind' element, please check your translation file. [junit] 13:36:50,386 INFO [TraceTranslator] inAContextDeclaration with 'context LegalAgreement inv : allInstances -> isUnique ( documentTitle )' [junit] 13:36:50,386 INFO [TraceTranslator] inAClassifierContextDeclaration with 'context LegalAgreement inv : allInstances -> isUnique ( documentTitle )' [junit] 13:36:50,406 INFO [TraceTranslator] inAClassifierExpressionBody with 'inv : allInstances -> isUnique ( documentTitle )' [junit] 13:36:50,416 INFO [TraceTranslator] inAInvClassifierStereotype with 'inv' [junit] 13:36:50,416 INFO [TraceTranslator] outAInvClassifierStereotype with 'inv' [junit] 13:36:50,416 INFO [TraceTranslator] inAExpression with 'allInstances -> isUnique ( documentTitle )' [junit] 13:36:50,416 INFO [TraceTranslator] inALogicalExpression with 'allInstances -> isUnique ( documentTitle )' [junit] 13:36:50,416 INFO [TraceTranslator] inARelationalExpression with 'allInstances -> isUnique ( documentTitle )' [junit] 13:36:50,416 INFO [TraceTranslator] inAAdditiveExpression with 'allInstances -> isUnique ( documentTitle )' [junit] 13:36:50,416 INFO [TraceTranslator] inAMultiplicativeExpression with 'allInstances -> isUnique ( documentTitle )' [junit] 13:36:50,426 INFO [TraceTranslator] inAPostfixUnaryExpression with 'allInstances -> isUnique ( documentTitle )' [junit] 13:36:50,426 INFO [TraceTranslator] inAPostfixExpression with 'allInstances -> isUnique ( documentTitle )' [junit] 13:36:50,426 INFO [TraceTranslator] inAFeaturePrimaryExpression with 'allInstances' [junit] 13:36:50,426 INFO [TraceTranslator] inAPathName with 'allInstances' [junit] 13:36:50,426 INFO [TraceTranslator] outAPathName with 'allInstances' [junit] 13:36:50,426 INFO [TraceTranslator] outAFeaturePrimaryExpression with 'allInstances' [junit] 13:36:50,446 INFO [TraceTranslator] inAArrowPostfixExpressionTail with '-> isUnique ( documentTitle )' [junit] 13:36:50,446 INFO [TraceTranslator] inAFeatureCall with 'isUnique ( documentTitle )' [junit] 13:36:50,446 INFO [TraceTranslator] inAPathName with 'isUnique' [junit] 13:36:50,446 INFO [TraceTranslator] outAPathName with 'isUnique' [junit] 13:36:50,446 INFO [TraceTranslator] inAFeatureCallParameters with '( documentTitle )' [junit] 13:36:50,446 INFO [TraceTranslator] inAActualParameterList with 'documentTitle' [junit] 13:36:50,446 INFO [TraceTranslator] inAExpression with 'documentTitle' [junit] 13:36:50,466 INFO [TraceTranslator] inALogicalExpression with 'documentTitle' [junit] 13:36:50,466 INFO [TraceTranslator] inARelationalExpression with 'documentTitle' [junit] 13:36:50,466 INFO [TraceTranslator] inAAdditiveExpression with 'documentTitle' [junit] 13:36:50,466 INFO [TraceTranslator] inAMultiplicativeExpression with 'documentTitle' [junit] 13:36:50,466 INFO [TraceTranslator] inAPostfixUnaryExpression with 'documentTitle' [junit] 13:36:50,466 INFO [TraceTranslator] inAPostfixExpression with 'documentTitle' [junit] 13:36:50,466 INFO [TraceTranslator] inAFeaturePrimaryExpression with 'documentTitle' [junit] 13:36:50,496 INFO [TraceTranslator] inAPathName with 'documentTitle' [junit] 13:36:50,496 INFO [TraceTranslator] outAPathName with 'documentTitle' [junit] 13:36:50,496 INFO [TraceTranslator] outAFeaturePrimaryExpression with 'documentTitle' [junit] 13:36:50,496 INFO [TraceTranslator] outAPostfixExpression with 'documentTitle' [junit] 13:36:50,496 INFO [TraceTranslator] outAPostfixUnaryExpression with 'documentTitle' [junit] 13:36:50,496 INFO [TraceTranslator] outAMultiplicativeExpression with 'documentTitle' [junit] 13:36:50,496 INFO [TraceTranslator] outAAdditiveExpression with 'documentTitle' [junit] 13:36:50,516 INFO [TraceTranslator] outARelationalExpression with 'documentTitle' [junit] 13:36:50,516 INFO [TraceTranslator] outALogicalExpression with 'documentTitle' [junit] 13:36:50,516 INFO [TraceTranslator] outAExpression with 'documentTitle' [junit] 13:36:50,516 INFO [TraceTranslator] outAActualParameterList with 'documentTitle' [junit] 13:36:50,516 INFO [TraceTranslator] outAFeatureCallParameters with '( documentTitle )' [junit] 13:36:50,516 INFO [TraceTranslator] outAFeatureCall with 'isUnique ( documentTitle )' [junit] 13:36:50,516 INFO [TraceTranslator] outAArrowPostfixExpressionTail with '-> isUnique ( documentTitle )' [junit] 13:36:50,546 INFO [TraceTranslator] outAPostfixExpression with 'allInstances -> isUnique ( documentTitle )' [junit] 13:36:50,546 INFO [TraceTranslator] outAPostfixUnaryExpression with 'allInstances -> isUnique ( documentTitle )' [junit] 13:36:50,546 INFO [TraceTranslator] outAMultiplicativeExpression with 'allInstances -> isUnique ( documentTitle )' [junit] 13:36:50,546 INFO [TraceTranslator] outAAdditiveExpression with 'allInstances -> isUnique ( documentTitle )' [junit] 13:36:50,546 INFO [TraceTranslator] outARelationalExpression with 'allInstances -> isUnique ( documentTitle )' [junit] 13:36:50,566 INFO [TraceTranslator] outALogicalExpression with 'allInstances -> isUnique ( documentTitle )' [junit] 13:36:50,566 INFO [TraceTranslator] outAExpression with 'allInstances -> isUnique ( documentTitle )' [junit] 13:36:50,566 INFO [TraceTranslator] outAClassifierExpressionBody with 'inv : allInstances -> isUnique ( documentTitle )' [junit] 13:36:50,566 INFO [TraceTranslator] outAClassifierContextDeclaration with 'context LegalAgreement inv : allInstances -> isUnique ( documentTitle )' [junit] 13:36:50,566 INFO [TraceTranslator] outAContextDeclaration with 'context LegalAgreement inv : allInstances -> isUnique ( documentTitle )' [junit] 13:36:50,566 INFO [TraceTranslator] ======================== Tracing Complete ========================
In the above output, each method name and its corresponding node value is shown.
(i.e. inAClassifierExpressionBody with 'allInstances -> isUnique ( documentTitle )'
says the method 'inAPostfixExpression' is being executed with value of
'allInstances -> isUnique ( documentTitle )'.) This means that if you want to
handle the 'inAPostfixExpression' output, you would override this method in your
ConstraintTranslator and call the
handleTranslationFragment(Node node).
Like so:
package org.ocltf.translation.impl; import org.ocltf.parser.node.APostfixExpression; import org.ocltf.translation.TranslationUtils; /** * Performs translation to the following: * <ul> * * <li> * Oracle-SQL * </li> * * </ul> * * @author brand028 */ public class ConstraintTranslator extends org.ocltf.translation.BaseTranslator { public void inAPostfixExpression(APostfixExpression expression) { //ideally, this is the only content that this method should contain //but you may need to do some processing before (or instead of) handling the expression. this.handleTranslationFragment(expression); } /*------------------------- all handler methods go below here -------------------------*/ }
Next you'll want to open your Oracle-SQL.vsl translation file and add the following:
'name'
attribute on the <kind/> element. You need
to specify the kind name as inv. You also need to give some content
to the kind element. (NOTE: If you'll remember from the previous step
the expression type we defined was of type inv. If the expression
had the type 'body'
, then the kind name would need to be body.
See the translation file schema for the possible
kind names.)
'name'
attribute on the <fragment/> element
with a name or regular expression that will be matched from the OCL expression
during translation. The ConstraintTranslator
is what will match these fragments during the translation process. Since our
constraint expression begins with allInstances
and we know that allInstances
can begin with self.allInstances
,
${element.name}.allInstances
, or just plain old allInstances
we'll want to give this fragment name
a regular expression that will match
on one of these three allInstances patterns.
NOTE: There is a goal called
ocltf:test-regex that will
allow you to test your regular expressions while writing them for your fragment names which
comes in very handy.
handlerMethod
attribute name with the name
of the method that will handle processing of this fragment. We'll call it
handleAllInstances
since that is what its doing.
<fragment name="(\s*(${elementName}|self)\s*\.)?\s*allInstances.*" handlerMethod="handleAllInstances"> <kind name="inv"> alter table $constraintTransformer.findTableName($element) add constraint </kind> </fragment>
We'll now add the handlerMethod definition to the Translator, what we'll do inside of this method is append the translation value (which is the body of the inv kind element from the previous step) to the expression being translated.
package org.ocltf.translation.impl; import org.ocltf.parser.node.APostfixExpression; import org.ocltf.translation.TranslationUtils; /** * Performs translation to the following: * <ul> * * <li> * Oracle-SQL * </li> * * </ul> * * @author brand028 */ public class ConstraintTranslator extends org.ocltf.translation.BaseTranslator { public void inAPostfixExpression(APostfixExpression expression) { //ideally, this is the only content that this method should contain //but you may need to do some processing before (or instead of) handling the expression. this.handleTranslationFragment(expression); } /*------------------------- all handler methods go below here -------------------------*/ /*------------------------- PostfixExpression Handler methods ---------------------*/ public void handleAllInstances(String translation, Object node) { //appended the value from the fragment to the expression buffer this.getExpression().appendToTranslatedExpression(translation); } }
Next we'll again run the ocltf:test-translation method, this time not adding the -Dtrace=true property.
maven ocltf:test-translation
Your console output should contain the following (in addition to other output). Notice the line: translated: --> 'alter table LegalAgrmt add constraint'. This line tells you the translation test executed and the 'translated' result was the new expression 'alter table LegalAgrmt add constraint'. Also look at the line: expected: --> ''. This is telling you your 'expected' translated result is an empty string (Your goal will be to get these two outputs to equal each other). Its an empty string because we have yet to add any content to the <to/> element of your TranslationTest-Oracle-SQL.xml file. We'll do that in the next step.
[junit] 22:08:40,527 INFO [ExpressionTranslationTest] translated: --> 'alter table LegalAgrmt add constraint' [junit] 22:08:40,527 INFO [ExpressionTranslationTest] expected: --> ''
Now we'll add what we expect the translated result to be. Open up your TranslationTest-Oracle-SQL.xml file, and add some content to the <to/> element body. What we are adding is what the 'from' element should be translated 'to' in Oracle SQL:
<expression contextType="org.ocltf.contracts.LegalAgreement"> <from> context LegalAgreement inv: self.allInstances -> isUnique(documentTitle) </from> <to> alter table LegalAgrmt add constraint UniqueLADT UNIQUE (Document_Title) </to> </expression>
For the final step of our little tutorial, we'll again run the translation test
maven goal: ocltf:test-translation to see if our 'translated' and 'expected' outputs
match (of course they won't since the only thing we have added to our
Oracle-SQL.vsl translation file so far is a fragment for
handling the allInstances
operation). This time you should at least see that
you have a value for your 'expected' output, and this is good, because you have something
to compare against while adding to your translation template
(Oracle-SQL.vsl) and translator
(ConstraintTranslator):
[junit] 12:53:52,174 INFO [ExpressionTranslationTest] translated: --> 'alter table LegalAgrmt add constraint' [junit] 12:53:52,174 INFO [ExpressionTranslationTest] expected: --> 'alter table LegalAgrmt add constraint UniqueLADT UNIQUE(Document_Title)'
Ok that's it! You're job now is to continue the process of getting the 'expected' and 'translated' expressions to equal each other (and get the Junit test cases to pass when running the Maven ocltf:test-translation goal) by repeating the steps above. To summarize you'll repeat the following steps over and over again until you have the translator and translations supporting the language you want.