Developing a Translation-Library

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.

Generating a Translation-Library

  1. 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
  2. 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:

    • NOTE: 'library' is the name of the Translation-Library and 'translations' is a comma seperated list of the translation languages you will be developing for this Translation-Library

    maven ocltf:generate-translation-library -Dlibrary=constraint -Dtranslations=Oracle-SQL
  3. 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):

    • IMPORTANT: each one of the resources that is generated at once by the maven ocltf:generate-translation-library goal can be generated seperately (you may want to do this after you've started working on your Translation-Library, if you want to add a new Translation, Translator, etc.). Please see the Maven OCLTF Plugin documentation for more information.

    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.
    • NOTE: The translation file is named 'Oracle-SQL' because if you'll remember we gave the 'translations' property a value of 'Oracle-SQL', you'll have a translation file generated for each comma seperated value specified for the 'translations' property when using the maven-ocltf-plugin ocltf:generate-translation-library goal.
    • IMPORTANT: Each translation file has an instance of the express's context element wrapped as an instance of ElementFacade in a scripting variable called element. It can be accessed in the standard velocity notation ${element}. This allows you to get any information you need from this element and make it available to your translation template during processing.
    src/java/org/ocltf/translation/constraint/ConstraintTranslator.java This is the java Translator class mapped within the Translation-Library descriptor: translation-library.xml
    • NOTE: As you'll notice if you look at the descriptor schema, you can have either one Translator instance per descriptor or you can override this default Translation-Library wide Translator with one per translation.
    • NOTE: Also note that the Translator generated is named 'ConstraintTranslator', this is again named after the library during the generation process.
    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.
    • NOTE: There will be a TranslationTest-*.xml file generated for every comma seperated value defined by the 'translation' property from the ocltf:generate-translation-library goal.
  4. 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
    .
  5. 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.

    • NOTE: If you're using Eclipse as your IDE you can execute the default Eclipse plugin goal: 'maven eclipse' within your Translation-Library directory and this will generate the necessary files for you to import your Translation-Library directory as an Eclipse project.

Adding Translation Capabilities to your Translation-Library

  1. 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>
    						
  2. 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 -------------------------*/
        
    }			
    					
    							

  3. Next you'll want to open your Oracle-SQL.vsl translation file and add the following:

    • Fill in the '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.)
    • Fill in the '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.
    • Fill in the 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.
    • Add the body of the inv kind element. NOTE: the expression $constraintTransformer.findTableName($element) allows us to find the SQL table name corresponding to the context ${element} scripting object always available to a translation template. You'll have to add the ${constraintTransformer} to your translation template by specifying it in your Translation-Library descriptor.
    	
    	<fragment name="(\s*(${elementName}|self)\s*\.)?\s*allInstances.*" handlerMethod="handleAllInstances">
    		<kind name="inv">
    			alter table $constraintTransformer.findTableName($element) add constraint	
    		</kind>	  
    	</fragment>
    						
  4. 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);
        }
        
    }			
    					
    							
  5. 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:   --> ''
    						
  6. 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>
    						
  7. 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)'
    						

Summary

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.

    Summary of Steps:
  1. Generate a Translation-Library if you don't have one.
  2. Add an OCL expression to a <from/> element in your translator test file (Add another <expression/> element to your test file if you already have one being used for a different test, take a look at the schema for what a TranslationTest-*.xml can contain).
  3. Execute the translator test with command maven ocltf:test-translation -Dtrace=true and decide what parser generated method you need to override in your Translator to handle a fragment of the expression: .
  4. Override the parser generated method and provide the method to handle a translation fragment.
  5. Edit your translation template and fill in the name, handlerMethod, kind name and kind body to your translation fragment (or add another fragment if you already have the first generated fragment defined, see the schema for more information) .
  6. Add the handlerMethod from the translation fragment (from previous step) to your Translator.
  7. Add the expected translated output to your translation test file as the body of the <to/> element.
  8. Execute the translation test with command maven ocltf:test-translation and see if the expressions equal each other and if the tests pass.
  9. Repeat steps 2 - 8 until you have the translator and translations supporting the language you want.

  • NOTE: You can view the source of the QueryTranslator for the query Translation-Library which may help in implementing your Translator.