View Javadoc
1   package org.andromda.translation.ocl.query;
2   
3   import java.util.Iterator;
4   import java.util.List;
5   import org.andromda.core.translation.TranslationUtils;
6   import org.andromda.translation.ocl.BaseTranslator;
7   import org.andromda.translation.ocl.node.AFeatureCall;
8   import org.andromda.translation.ocl.node.ALogicalExpressionTail;
9   import org.andromda.translation.ocl.node.AParenthesesPrimaryExpression;
10  import org.andromda.translation.ocl.node.APropertyCallExpression;
11  import org.andromda.translation.ocl.node.ARelationalExpressionTail;
12  import org.andromda.translation.ocl.node.AStandardDeclarator;
13  import org.andromda.translation.ocl.node.PRelationalExpression;
14  import org.andromda.translation.ocl.syntax.ConcreteSyntaxUtils;
15  import org.apache.commons.lang.StringUtils;
16  
17  /**
18   * Performs translation to the following: <ul> <li>Hibernate-QL</li> </ul>
19   *
20   * @author Chad Brandon
21   */
22  public class QueryTranslator
23          extends BaseTranslator
24  {
25      /**
26       * Contains the select clause of the query.
27       */
28      private StringBuffer selectClause;
29  
30      /**
31       * Called by super class to reset any objects.
32       */
33      public void preProcess()
34      {
35          super.preProcess();
36          this.selectClause = new StringBuffer();
37          this.sortedByClause = new StringBuffer();
38          this.declaratorCtr = 0;
39      }
40  
41      /**
42       * Stores the name of the initial declarator.
43       */
44      private short declaratorCtr;
45  
46      /**
47       * True/false whether or not its the initial declarator. We care about this because we must know what the first one
48       * is for differentiating between the first declarator (the beginning of the query) and any other declarators (the
49       * connecting associations).
50       * @return isInitialDeclarator
51       */
52      protected boolean isInitialDeclarator()
53      {
54          boolean isInitialDeclarator = (this.declaratorCtr <= 0);
55          this.declaratorCtr++;
56          return isInitialDeclarator;
57      }
58  
59      /**
60       * Helps out with 'includesAll', replaces the {1} in the expression fragment when a declarator is encountered (i.e.
61       * '| <variable name>')
62       * @param declarator
63       */
64      public void inAStandardDeclarator(AStandardDeclarator declarator)
65      {
66          final String methodName = "QueryTranslator.inAStandardDeclarator";
67          if (logger.isDebugEnabled())
68              logger.debug("performing " + methodName + " with declarator --> " + declarator);
69  
70          String temp = this.selectClause.toString();
71  
72          String declaratorName = ConcreteSyntaxUtils.getVariableDeclarations(declarator)[0].getName();
73  
74          // by default we'll assume we're replacing the {1} arg.
75          short replacement = 1;
76          if (this.isInitialDeclarator())
77          {
78              // handle differently if its the initial declarator,
79              // replacement is {0}
80              replacement = 0;
81          }
82  
83          // now replace {1} reference
84          temp = TranslationUtils.replacePattern(temp, String.valueOf(replacement), declaratorName);
85          this.selectClause = new StringBuffer(temp);
86      }
87  
88      /**
89       * Override to handle any propertyCall expressions ( i.e. includes( <expression>), select( <expression>), etc.)
90       * @param expression
91       * @see BaseTranslator#handleTranslationFragment(Object)
92       */
93      public void inAPropertyCallExpression(APropertyCallExpression expression)
94      {
95          this.handleTranslationFragment(expression);
96      }
97  
98      /**
99       * Override to handle any featureCall expressions ( i.e. sortedBy( <expression>), etc.)
100      * @param expression
101      * @see BaseTranslator#handleTranslationFragment(Object)
102      */
103     public void inAFeatureCall(AFeatureCall expression)
104     {
105         // don't handle all instances here, since it's handled
106         // in the property call expression.
107         if (!TranslationUtils.trimToEmpty(expression).matches(OCLPatterns.ALL_INSTANCES))
108         {
109             this.handleTranslationFragment(expression);
110         }
111     }
112 
113     /**
114      * Override to deal with logical 'and, 'or', 'xor, ... expressions.
115      * @param logicalExpressionTail
116      * @see BaseTranslator#handleTranslationFragment(Object)
117      */
118     public void inALogicalExpressionTail(ALogicalExpressionTail logicalExpressionTail)
119     {
120         this.handleTranslationFragment(logicalExpressionTail);
121     }
122 
123     /**
124      * Override to deal with relational ' <, '>', '=', ... expressions.
125      * @param relationalExpressionTail
126      * @see BaseTranslator#handleTranslationFragment(Object)
127      */
128     public void inARelationalExpressionTail(ARelationalExpressionTail relationalExpressionTail)
129     {
130         this.handleTranslationFragment(relationalExpressionTail);
131     }
132 
133     /**
134      * Override to deal with entering parenthesis expressions '( <expression>)'.
135      * @param expression
136      * @see org.andromda.core.translation.Expression#appendToTranslatedExpression(Object)
137      */
138     public void inAParenthesesPrimaryExpression(AParenthesesPrimaryExpression expression)
139     {
140         this.getExpression().appendSpaceToTranslatedExpression();
141         this.getExpression().appendToTranslatedExpression("(");
142     }
143 
144     /**
145      * Override to deal with leaving parenthesis expressions '( <expression>)'.
146      * @param expression
147      * @see org.andromda.core.translation.Expression#appendToTranslatedExpression(Object)
148      */
149     public void outAParenthesesPrimaryExpression(AParenthesesPrimaryExpression expression)
150     {
151         this.getExpression().appendSpaceToTranslatedExpression();
152         this.getExpression().appendToTranslatedExpression(")");
153     }
154 
155     /**
156      * Checks to see if the replacement is an argument and if so replaces the {index} in the fragment with the
157      * 'argument' fragment from the template. Otherwise replaces the {index} with the passed in replacement value.
158      *
159      * @param fragment    the fragment to perform replacement.
160      * @param replacement the replacement string
161      * @param index       the index in the string to replace.
162      * @return String the fragment with any replacements.
163      */
164     protected String replaceFragment(String fragment, String replacement, int index)
165     {
166         fragment = StringUtils.trimToEmpty(fragment);
167         replacement = StringUtils.trimToEmpty(replacement);
168         // if 'replacement' is an argument use that for the replacement
169         // in the fragment
170         if (this.isOperationArgument(replacement))
171         {
172             final String argument = replacement;
173             replacement = this.getTranslationFragment("argument");
174             replacement = TranslationUtils.replacePattern(replacement, String.valueOf(0), argument);
175         }
176         fragment = TranslationUtils.replacePattern(fragment, String.valueOf(index), replacement);
177         return fragment;
178     }
179 
180     /**
181      * Stores the name of the fragment that maps the tail of the select clause.
182      */
183     private static final String SELECT_CLAUSE_TAIL = "selectClauseTail";
184 
185     /**
186      * Stores the name of the fragment that maps to the head of the sortedBy clause.
187      */
188     private static final String SORTED_BY_CLAUSE_HEAD = "sortedByClauseHead";
189 
190     /**
191      * Handles any final processing.
192      */
193     @Override
194     public void postProcess()
195     {
196         // create the final translated expression
197         String selectClauseTail = this.getTranslationFragment(SELECT_CLAUSE_TAIL);
198         String existingExpression = StringUtils.trimToEmpty(this.getExpression().getTranslatedExpression());
199 
200         if (StringUtils.isNotBlank(selectClauseTail) && StringUtils.isNotBlank(existingExpression))
201         {
202             this.selectClause.append(' ');
203             this.selectClause.append(selectClauseTail);
204             this.selectClause.append(' ');
205         }
206 
207         this.getExpression().insertInTranslatedExpression(0, selectClause.toString());
208 
209         if (this.sortedByClause.length() > 0)
210         {
211             this.getExpression().appendSpaceToTranslatedExpression();
212             this.getExpression().appendToTranslatedExpression(this.getTranslationFragment(SORTED_BY_CLAUSE_HEAD));
213             this.getExpression().appendSpaceToTranslatedExpression();
214             this.getExpression().appendToTranslatedExpression(this.sortedByClause);
215         }
216 
217         // remove any extra space from parenthesis
218         this.getExpression().replaceInTranslatedExpression("\\(\\s*", "(");
219         this.getExpression().replaceInTranslatedExpression("\\s*\\)", ")");
220     }
221 
222     /*------------------------- Handler methods ---------------------------------------*/
223 
224     /*------------------------- PropertyCallExpression Handler methods ---------------------*/
225 
226     /**
227      * @param translation
228      * @param node
229      */
230     public void handleSubSelect(String translation, Object node)
231     {
232         APropertyCallExpression propertyCallExpression = (APropertyCallExpression) node;
233 
234         String primaryExpression = ConcreteSyntaxUtils.getPrimaryExpression(propertyCallExpression);
235 
236         // set the association which the 'includesAll' indicates (which
237         // is the first feature call from the list of feature calls)
238         translation = this.replaceFragment(translation, TranslationUtils.trimToEmpty(primaryExpression), 0);
239 
240         this.selectClause.append(' ');
241         this.selectClause.append(translation);
242     }
243 
244     /**
245      * @param translation
246      * @param node
247      */
248     public void handleIsLike(String translation, Object node)
249     {
250         APropertyCallExpression propertyCallExpression = (APropertyCallExpression) node;
251         List featureCalls = ConcreteSyntaxUtils.getFeatureCalls(propertyCallExpression);
252 
253         List params = ConcreteSyntaxUtils.getParameters((AFeatureCall) featureCalls.get(0));
254 
255         translation = this.replaceFragment(translation, TranslationUtils.deleteWhitespace(params.get(0)), 0);
256         translation = this.replaceFragment(translation, TranslationUtils.deleteWhitespace(params.get(1)), 1);
257 
258         if (StringUtils.isNotBlank(this.getExpression().getTranslatedExpression()))
259         {
260             this.getExpression().appendSpaceToTranslatedExpression();
261         }
262         this.getExpression().appendToTranslatedExpression(translation);
263     }
264 
265     /**
266      * @param translation
267      * @param node
268      */
269     public void handleSelect(String translation, Object node)
270     {
271         this.selectClause.append(translation);
272     }
273 
274     /**
275      * @param translation
276      * @param node
277      */
278     public void handleIncludes(String translation, Object node)
279     {
280         APropertyCallExpression propertyCallExpression = (APropertyCallExpression) node;
281         List featureCalls = ConcreteSyntaxUtils.getFeatureCalls(propertyCallExpression);
282 
283         // since the parameters can only be either dotFeatureCall or
284         // arrowFeatureCall we try one or the other.
285         String parameters = StringUtils.deleteWhitespace(ConcreteSyntaxUtils.getParametersAsString(
286                 (AFeatureCall) featureCalls.get(0)));
287 
288         String primaryExpression = ConcreteSyntaxUtils.getPrimaryExpression(propertyCallExpression);
289 
290         translation = this.replaceFragment(translation, primaryExpression, 1);
291         translation = this.replaceFragment(translation, parameters, 0);
292 
293         this.getExpression().appendSpaceToTranslatedExpression();
294         this.getExpression().appendToTranslatedExpression(translation);
295     }
296 
297     /**
298      * @param translation
299      * @param node
300      */
301     public void handleDotOperation(String translation, Object node)
302     {
303         APropertyCallExpression propertyCallExpression = (APropertyCallExpression) node;
304         String firstArgument = ConcreteSyntaxUtils.getPrimaryExpression(propertyCallExpression);
305         translation = this.replaceFragment(translation, firstArgument, 0);
306         List featureCalls = ConcreteSyntaxUtils.getFeatureCalls(propertyCallExpression);
307         if (featureCalls != null && !featureCalls.isEmpty())
308         {
309             // here we loop through the feature calls and find the ones
310             // that are operation feature calls, we then retrieve and replace
311             // all parameters in the translated expression
312             for (final Iterator callIterator = featureCalls.iterator(); callIterator.hasNext();)
313             {
314                 AFeatureCall featureCall = (AFeatureCall) callIterator.next();
315 
316                 if (TranslationUtils.trimToEmpty(featureCall).matches(OCLPatterns.OPERATION_FEATURE_CALL))
317                 {
318                     List parameters = ConcreteSyntaxUtils.getParameters(featureCall);
319                     if (parameters != null && !parameters.isEmpty())
320                     {
321                         Iterator parameterIterator = parameters.iterator();
322                         for (int ctr = 1; parameterIterator.hasNext(); ctr++)
323                         {
324                             translation = this.replaceFragment(translation, (String) parameterIterator.next(), ctr);
325                         }
326                     }
327                     break;
328                 }
329             }
330         }
331         this.getExpression().appendSpaceToTranslatedExpression();
332         this.getExpression().appendToTranslatedExpression(translation);
333     }
334 
335     private StringBuffer sortedByClause;
336 
337     /**
338      * @param translation
339      * @param node
340      */
341     public void handleSortedBy(String translation, Object node)
342     {
343         if (this.sortedByClause.length() > 0)
344         {
345             this.sortedByClause.append(", ");
346         }
347         this.sortedByClause.append(TranslationUtils.deleteWhitespace(ConcreteSyntaxUtils.getParametersAsString(
348                 (AFeatureCall) node)));
349     }
350 
351     /*------------------------- Logical Expression Handler (and, or, xor, etc.) ----------------------*/
352 
353     /**
354      * @param translation
355      * @param node
356      */
357     public void handleLogicalExpression(String translation, Object node)
358     {
359         this.getExpression().appendSpaceToTranslatedExpression();
360         this.getExpression().appendToTranslatedExpression(translation);
361     }
362 
363     /*------------------------- Relational Expression Handler (=, <, >, >=, etc.) --------------------*/
364 
365     /**
366      * @param translation
367      * @param node
368      */
369     public void handleRelationalExpression(String translation, Object node)
370     {
371         ARelationalExpressionTail relationalExpressionTail = (ARelationalExpressionTail) node;
372 
373         String[] leftAndRightExpressions = ConcreteSyntaxUtils.getLeftAndRightExpressions(
374                 (PRelationalExpression) relationalExpressionTail.parent());
375         String leftExpression = StringUtils.deleteWhitespace(leftAndRightExpressions[0]);
376         String rightExpression = StringUtils.deleteWhitespace(leftAndRightExpressions[1]);
377         if (leftExpression.matches(OCLPatterns.OPERATION_FEATURE_CALL))
378         {
379             leftExpression = "";
380         }
381         translation = this.replaceFragment(translation, leftExpression, 0);
382         if (rightExpression.matches(OCLPatterns.OPERATION_FEATURE_CALL))
383         {
384             rightExpression = "";
385         }
386         translation = this.replaceFragment(translation, rightExpression, 1);
387 
388         this.getExpression().appendSpaceToTranslatedExpression();
389         this.getExpression().appendToTranslatedExpression(translation);
390     }
391 }