001package org.andromda.translation.ocl.query; 002 003import java.util.Iterator; 004import java.util.List; 005import org.andromda.core.translation.TranslationUtils; 006import org.andromda.translation.ocl.BaseTranslator; 007import org.andromda.translation.ocl.node.AFeatureCall; 008import org.andromda.translation.ocl.node.ALogicalExpressionTail; 009import org.andromda.translation.ocl.node.AParenthesesPrimaryExpression; 010import org.andromda.translation.ocl.node.APropertyCallExpression; 011import org.andromda.translation.ocl.node.ARelationalExpressionTail; 012import org.andromda.translation.ocl.node.AStandardDeclarator; 013import org.andromda.translation.ocl.node.PRelationalExpression; 014import org.andromda.translation.ocl.syntax.ConcreteSyntaxUtils; 015import org.apache.commons.lang.StringUtils; 016 017/** 018 * Performs translation to the following: <ul> <li>Hibernate-QL</li> </ul> 019 * 020 * @author Chad Brandon 021 */ 022public class QueryTranslator 023 extends BaseTranslator 024{ 025 /** 026 * Contains the select clause of the query. 027 */ 028 private StringBuffer selectClause; 029 030 /** 031 * Called by super class to reset any objects. 032 */ 033 public void preProcess() 034 { 035 super.preProcess(); 036 this.selectClause = new StringBuffer(); 037 this.sortedByClause = new StringBuffer(); 038 this.declaratorCtr = 0; 039 } 040 041 /** 042 * Stores the name of the initial declarator. 043 */ 044 private short declaratorCtr; 045 046 /** 047 * True/false whether or not its the initial declarator. We care about this because we must know what the first one 048 * is for differentiating between the first declarator (the beginning of the query) and any other declarators (the 049 * connecting associations). 050 * @return isInitialDeclarator 051 */ 052 protected boolean isInitialDeclarator() 053 { 054 boolean isInitialDeclarator = (this.declaratorCtr <= 0); 055 this.declaratorCtr++; 056 return isInitialDeclarator; 057 } 058 059 /** 060 * Helps out with 'includesAll', replaces the {1} in the expression fragment when a declarator is encountered (i.e. 061 * '| <variable name>') 062 * @param declarator 063 */ 064 public void inAStandardDeclarator(AStandardDeclarator declarator) 065 { 066 final String methodName = "QueryTranslator.inAStandardDeclarator"; 067 if (logger.isDebugEnabled()) 068 logger.debug("performing " + methodName + " with declarator --> " + declarator); 069 070 String temp = this.selectClause.toString(); 071 072 String declaratorName = ConcreteSyntaxUtils.getVariableDeclarations(declarator)[0].getName(); 073 074 // by default we'll assume we're replacing the {1} arg. 075 short replacement = 1; 076 if (this.isInitialDeclarator()) 077 { 078 // handle differently if its the initial declarator, 079 // replacement is {0} 080 replacement = 0; 081 } 082 083 // now replace {1} reference 084 temp = TranslationUtils.replacePattern(temp, String.valueOf(replacement), declaratorName); 085 this.selectClause = new StringBuffer(temp); 086 } 087 088 /** 089 * Override to handle any propertyCall expressions ( i.e. includes( <expression>), select( <expression>), etc.) 090 * @param expression 091 * @see BaseTranslator#handleTranslationFragment(Object) 092 */ 093 public void inAPropertyCallExpression(APropertyCallExpression expression) 094 { 095 this.handleTranslationFragment(expression); 096 } 097 098 /** 099 * 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}