View Javadoc

1   package org.andromda.translation.ocl.query;
2   
3   import org.andromda.core.translation.TranslationUtils;
4   import org.andromda.translation.ocl.BaseTranslator;
5   import org.andromda.translation.ocl.node.AFeatureCall;
6   import org.andromda.translation.ocl.node.ALogicalExpressionTail;
7   import org.andromda.translation.ocl.node.AParenthesesPrimaryExpression;
8   import org.andromda.translation.ocl.node.APropertyCallExpression;
9   import org.andromda.translation.ocl.node.ARelationalExpressionTail;
10  import org.andromda.translation.ocl.node.AStandardDeclarator;
11  import org.andromda.translation.ocl.node.PRelationalExpression;
12  import org.andromda.translation.ocl.syntax.ConcreteSyntaxUtils;
13  import org.apache.commons.lang.StringUtils;
14  
15  import java.util.Iterator;
16  import java.util.List;
17  
18  /***
19   * Performs translation to the following: <ul> <li>Hibernate-QL</li> </ul>
20   *
21   * @author Chad Brandon
22   */
23  public class QueryTranslator
24          extends BaseTranslator
25  {
26  
27      /***
28       * Contains the select clause of the query.
29       */
30      private StringBuffer selectClause;
31  
32      /***
33       * Called by super class to reset any objects.
34       */
35      public void preProcess()
36      {
37          super.preProcess();
38          this.selectClause = new StringBuffer();
39          this.sortedByClause = new StringBuffer();
40          this.declaratorCtr = 0;
41      }
42  
43      /***
44       * Stores the name of the initial declarator.
45       */
46      private short declaratorCtr;
47  
48      /***
49       * True/false whether or not its the initial declarator. We care about this because we must know what the first one
50       * is for differentiating between the first declarator (the beginning of the query) and any other declarators (the
51       * connecting associations).
52       */
53      protected boolean isInitialDeclarator()
54      {
55          boolean isInitialDeclarator = (this.declaratorCtr <= 0);
56          this.declaratorCtr++;
57          return isInitialDeclarator;
58      }
59  
60      /***
61       * Helps out with 'includesAll', replaces the {1} in the expression fragment when a declarator is encountered (i.e.
62       * '| <variable name>')
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       *
91       * @see org.andromda.core.translation.analysis.DepthFirstAdapter#inAPropertyCallExpression(org.andromda.core.translation.node.APropertyCallExpression)
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      *
101      * @see org.andromda.core.translation.analysis.DepthFirstAdapter#inAFeatureCallExpression(org.andromda.core.translation.node.APropertyCallExpression)
102      */
103     public void inAFeatureCall(AFeatureCall expression)
104     {
105         // don't handl 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      *
116      * @see org.andromda.core.translation.analysis.DepthFirstAdapter#inALogicalExpressionTail(org.andromda.core.translation.node.ALogicalExpressionTail)
117      */
118     public void inALogicalExpressionTail(ALogicalExpressionTail logicalExpressionTail)
119     {
120         this.handleTranslationFragment(logicalExpressionTail);
121     }
122 
123     /***
124      * Override to deal with relational ' <, '>', '=', ... expressions.
125      *
126      * @see org.andromda.core.translation.analysis.DepthFirstAdapter#inARelationalExpressionTail(org.andromda.core.translation.node.ARelationalExpressionTail)
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      *
136      * @see org.andromda.core.translation.analysis.DepthFirstAdapter#inAParenthesesPrimaryExpression(org.andromda.core.translation.node.AParenthesesPrimaryExpression)
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      *
147      * @see org.andromda.core.translation.analysis.DepthFirstAdapter#outAParenthesesPrimaryExpression(org.andromda.core.translation.node.AParenthesesPrimaryExpression)
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     public void postProcess()
194     {
195         super.postProcess();
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.isNotEmpty(selectClauseTail) && StringUtils.isNotEmpty(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     public void handleSubSelect(String translation, Object node)
227     {
228         APropertyCallExpression propertyCallExpression = (APropertyCallExpression)node;
229 
230         String primaryExpression = ConcreteSyntaxUtils.getPrimaryExpression(propertyCallExpression);
231 
232         // set the association which the 'includesAll' indicates (which
233         // is the first feature call from the list of feature calls)
234         translation = this.replaceFragment(translation, TranslationUtils.trimToEmpty(primaryExpression), 0);
235 
236         this.selectClause.append(" ");
237         this.selectClause.append(translation);
238     }
239 
240     public void handleIsLike(String translation, Object node)
241     {
242         APropertyCallExpression propertyCallExpression = (APropertyCallExpression)node;
243         List featureCalls = ConcreteSyntaxUtils.getFeatureCalls(propertyCallExpression);
244 
245         List params = params = ConcreteSyntaxUtils.getParameters((AFeatureCall)featureCalls.get(0));
246 
247         translation = this.replaceFragment(translation, TranslationUtils.deleteWhitespace(params.get(0)), 0);
248         translation = this.replaceFragment(translation, TranslationUtils.deleteWhitespace(params.get(1)), 1);
249 
250         if (StringUtils.isNotEmpty(this.getExpression().getTranslatedExpression()))
251         {
252             this.getExpression().appendSpaceToTranslatedExpression();
253         }
254         this.getExpression().appendToTranslatedExpression(translation);
255     }
256 
257     public void handleSelect(String translation, Object node)
258     {
259         this.selectClause.append(translation);
260     }
261 
262     public void handleIncludes(String translation, Object node)
263     {
264         APropertyCallExpression propertyCallExpression = (APropertyCallExpression)node;
265         List featureCalls = ConcreteSyntaxUtils.getFeatureCalls(propertyCallExpression);
266 
267         // since the parameters can only be either dotFeatureCall or
268         // arrowFeatureCall we try one or the other.
269         String parameters = StringUtils.deleteWhitespace(ConcreteSyntaxUtils.getParametersAsString(
270                 (AFeatureCall)featureCalls.get(0)));
271 
272         String primaryExpression = ConcreteSyntaxUtils.getPrimaryExpression(propertyCallExpression);
273 
274         translation = this.replaceFragment(translation, primaryExpression, 1);
275         translation = this.replaceFragment(translation, parameters, 0);
276 
277         this.getExpression().appendSpaceToTranslatedExpression();
278         this.getExpression().appendToTranslatedExpression(translation);
279     }
280 
281     public void handleDotOperation(String translation, Object node)
282     {
283         APropertyCallExpression propertyCallExpression = (APropertyCallExpression)node;
284         String firstArgument = ConcreteSyntaxUtils.getPrimaryExpression(propertyCallExpression);
285         translation = this.replaceFragment(translation, firstArgument, 0);
286         List featureCalls = ConcreteSyntaxUtils.getFeatureCalls(propertyCallExpression);
287         if (featureCalls != null && !featureCalls.isEmpty())
288         {
289             // here we loop through the feature calls and find the ones
290             // that are operation feature calls, we then retrieve and replace
291             // all parameters in the translated expression
292             for (final Iterator callIterator = featureCalls.iterator(); callIterator.hasNext();)
293             {
294                 AFeatureCall featureCall = (AFeatureCall)callIterator.next();
295 
296                 if (TranslationUtils.trimToEmpty(featureCall).matches(OCLPatterns.OPERATION_FEATURE_CALL))
297                 {
298                     List parameters = ConcreteSyntaxUtils.getParameters(featureCall);
299                     if (parameters != null && !parameters.isEmpty())
300                     {
301                         Iterator parameterIterator = parameters.iterator();
302                         for (int ctr = 1; parameterIterator.hasNext(); ctr++)
303                         {
304                             translation = this.replaceFragment(translation, (String)parameterIterator.next(), ctr);
305                         }
306                     }
307                     break;
308                 }
309             }
310         }
311         this.getExpression().appendSpaceToTranslatedExpression();
312         this.getExpression().appendToTranslatedExpression(translation);
313     }
314 
315     private StringBuffer sortedByClause;
316 
317     public void handleSortedBy(String translation, Object node)
318     {
319         if (this.sortedByClause.length() > 0)
320         {
321             this.sortedByClause.append(", ");
322         }
323         this.sortedByClause.append(TranslationUtils.deleteWhitespace(ConcreteSyntaxUtils.getParametersAsString(
324                 (AFeatureCall)node)));
325     }
326 
327     /*------------------------- Logical Expression Handler (and, or, xor, etc.) ----------------------*/
328 
329     public void handleLogicalExpression(String translation, Object node)
330     {
331         this.getExpression().appendSpaceToTranslatedExpression();
332         this.getExpression().appendToTranslatedExpression(translation);
333     }
334 
335     /*------------------------- Relational Expression Handler (=, <, >, >=, etc.) --------------------*/
336 
337     public void handleRelationalExpression(String translation, Object node)
338     {
339         ARelationalExpressionTail relationalExpressionTail = (ARelationalExpressionTail)node;
340 
341         String[] leftAndRightExpressions = ConcreteSyntaxUtils.getLeftAndRightExpressions(
342                 (PRelationalExpression)relationalExpressionTail.parent());
343         String leftExpression = StringUtils.deleteWhitespace(leftAndRightExpressions[0]);
344         String rightExpression = StringUtils.deleteWhitespace(leftAndRightExpressions[1]);
345         if (leftExpression.matches(OCLPatterns.OPERATION_FEATURE_CALL))
346         {
347             leftExpression = "";
348         }
349         translation = this.replaceFragment(translation, leftExpression, 0);
350         if (rightExpression.matches(OCLPatterns.OPERATION_FEATURE_CALL))
351         {
352             rightExpression = "";
353         }
354         translation = this.replaceFragment(translation, rightExpression, 1);
355 
356         this.getExpression().appendSpaceToTranslatedExpression();
357         this.getExpression().appendToTranslatedExpression(translation);
358     }
359 }