001: package org.andromda.translation.ocl.query;
002:
003: import org.andromda.core.translation.TranslationUtils;
004: import org.andromda.translation.ocl.BaseTranslator;
005: import org.andromda.translation.ocl.node.AFeatureCall;
006: import org.andromda.translation.ocl.node.ALogicalExpressionTail;
007: import org.andromda.translation.ocl.node.AParenthesesPrimaryExpression;
008: import org.andromda.translation.ocl.node.APropertyCallExpression;
009: import org.andromda.translation.ocl.node.ARelationalExpressionTail;
010: import org.andromda.translation.ocl.node.AStandardDeclarator;
011: import org.andromda.translation.ocl.node.PRelationalExpression;
012: import org.andromda.translation.ocl.syntax.ConcreteSyntaxUtils;
013: import org.apache.commons.lang.StringUtils;
014:
015: import java.util.Iterator;
016: import java.util.List;
017:
018: /**
019: * Performs translation to the following: <ul> <li>Hibernate-QL</li> </ul>
020: *
021: * @author Chad Brandon
022: */
023: public class QueryTranslator 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: super .preProcess();
035: this .selectClause = new StringBuffer();
036: this .sortedByClause = new StringBuffer();
037: this .declaratorCtr = 0;
038: }
039:
040: /**
041: * Stores the name of the initial declarator.
042: */
043: private short declaratorCtr;
044:
045: /**
046: * True/false whether or not its the initial declarator. We care about this because we must know what the first one
047: * is for differentiating between the first declarator (the beginning of the query) and any other declarators (the
048: * connecting associations).
049: */
050: protected boolean isInitialDeclarator() {
051: boolean isInitialDeclarator = (this .declaratorCtr <= 0);
052: this .declaratorCtr++;
053: return isInitialDeclarator;
054: }
055:
056: /**
057: * Helps out with 'includesAll', replaces the {1} in the expression fragment when a declarator is encountered (i.e.
058: * '| <variable name>')
059: */
060: public void inAStandardDeclarator(AStandardDeclarator declarator) {
061: final String methodName = "QueryTranslator.inAStandardDeclarator";
062: if (logger.isDebugEnabled())
063: logger.debug("performing " + methodName
064: + " with declarator --> " + declarator);
065:
066: String temp = this .selectClause.toString();
067:
068: String declaratorName = ConcreteSyntaxUtils
069: .getVariableDeclarations(declarator)[0].getName();
070:
071: // by default we'll assume we're replacing the {1} arg.
072: short replacement = 1;
073: if (this .isInitialDeclarator()) {
074: // handle differently if its the initial declarator,
075: // replacement is {0}
076: replacement = 0;
077: }
078:
079: // now replace {1} reference
080: temp = TranslationUtils.replacePattern(temp, String
081: .valueOf(replacement), declaratorName);
082: this .selectClause = new StringBuffer(temp);
083: }
084:
085: /**
086: * Override to handle any propertyCall expressions ( i.e. includes( <expression>), select( <expression>), etc.)
087: *
088: * @see org.andromda.core.translation.analysis.DepthFirstAdapter#inAPropertyCallExpression(org.andromda.core.translation.node.APropertyCallExpression)
089: */
090: public void inAPropertyCallExpression(
091: APropertyCallExpression expression) {
092: this .handleTranslationFragment(expression);
093: }
094:
095: /**
096: * Override to handle any featureCall expressions ( i.e. sortedBy( <expression>), etc.)
097: *
098: * @see org.andromda.core.translation.analysis.DepthFirstAdapter#inAFeatureCallExpression(org.andromda.core.translation.node.APropertyCallExpression)
099: */
100: public void inAFeatureCall(AFeatureCall expression) {
101: // don't handl all instances here, since it's handled
102: // in the property call expression.
103: if (!TranslationUtils.trimToEmpty(expression).matches(
104: OCLPatterns.ALL_INSTANCES)) {
105: this .handleTranslationFragment(expression);
106: }
107: }
108:
109: /**
110: * Override to deal with logical 'and, 'or', 'xor, ... expressions.
111: *
112: * @see org.andromda.core.translation.analysis.DepthFirstAdapter#inALogicalExpressionTail(org.andromda.core.translation.node.ALogicalExpressionTail)
113: */
114: public void inALogicalExpressionTail(
115: ALogicalExpressionTail logicalExpressionTail) {
116: this .handleTranslationFragment(logicalExpressionTail);
117: }
118:
119: /**
120: * Override to deal with relational ' <, '>', '=', ... expressions.
121: *
122: * @see org.andromda.core.translation.analysis.DepthFirstAdapter#inARelationalExpressionTail(org.andromda.core.translation.node.ARelationalExpressionTail)
123: */
124: public void inARelationalExpressionTail(
125: ARelationalExpressionTail relationalExpressionTail) {
126: this .handleTranslationFragment(relationalExpressionTail);
127: }
128:
129: /**
130: * Override to deal with entering parenthesis expressions '( <expression>)'.
131: *
132: * @see org.andromda.core.translation.analysis.DepthFirstAdapter#inAParenthesesPrimaryExpression(org.andromda.core.translation.node.AParenthesesPrimaryExpression)
133: */
134: public void inAParenthesesPrimaryExpression(
135: AParenthesesPrimaryExpression expression) {
136: this .getExpression().appendSpaceToTranslatedExpression();
137: this .getExpression().appendToTranslatedExpression("(");
138: }
139:
140: /**
141: * Override to deal with leaving parenthesis expressions '( <expression>)'.
142: *
143: * @see org.andromda.core.translation.analysis.DepthFirstAdapter#outAParenthesesPrimaryExpression(org.andromda.core.translation.node.AParenthesesPrimaryExpression)
144: */
145: public void outAParenthesesPrimaryExpression(
146: AParenthesesPrimaryExpression expression) {
147: this .getExpression().appendSpaceToTranslatedExpression();
148: this .getExpression().appendToTranslatedExpression(")");
149: }
150:
151: /**
152: * Checks to see if the replacement is an argument and if so replaces the {index} in the fragment with the
153: * 'argument' fragment from the template. Otherwise replaces the {index} with the passed in replacement value.
154: *
155: * @param fragment the fragment to perform replacement.
156: * @param replacement the replacement string
157: * @param index the index in the string to replace.
158: * @return String the fragment with any replacements.
159: */
160: protected String replaceFragment(String fragment,
161: String replacement, int index) {
162: fragment = StringUtils.trimToEmpty(fragment);
163: replacement = StringUtils.trimToEmpty(replacement);
164: // if 'replacement' is an argument use that for the replacement
165: // in the fragment
166: if (this .isOperationArgument(replacement)) {
167: final String argument = replacement;
168: replacement = this .getTranslationFragment("argument");
169: replacement = TranslationUtils.replacePattern(replacement,
170: String.valueOf(0), argument);
171: }
172: fragment = TranslationUtils.replacePattern(fragment, String
173: .valueOf(index), replacement);
174: return fragment;
175: }
176:
177: /**
178: * Stores the name of the fragment that maps the tail of the select clause.
179: */
180: private static final String SELECT_CLAUSE_TAIL = "selectClauseTail";
181:
182: /**
183: * Stores the name of the fragment that maps to the head of the sortedBy clause.
184: */
185: private static final String SORTED_BY_CLAUSE_HEAD = "sortedByClauseHead";
186:
187: /**
188: * Handles any final processing.
189: */
190: public void postProcess() {
191: super .postProcess();
192: // create the final translated expression
193: String selectClauseTail = this
194: .getTranslationFragment(SELECT_CLAUSE_TAIL);
195: String existingExpression = StringUtils.trimToEmpty(this
196: .getExpression().getTranslatedExpression());
197:
198: if (StringUtils.isNotEmpty(selectClauseTail)
199: && StringUtils.isNotEmpty(existingExpression)) {
200: this .selectClause.append(" ");
201: this .selectClause.append(selectClauseTail);
202: this .selectClause.append(" ");
203: }
204:
205: this .getExpression().insertInTranslatedExpression(0,
206: selectClause.toString());
207:
208: if (this .sortedByClause.length() > 0) {
209: this .getExpression().appendSpaceToTranslatedExpression();
210: this .getExpression().appendToTranslatedExpression(
211: this .getTranslationFragment(SORTED_BY_CLAUSE_HEAD));
212: this .getExpression().appendSpaceToTranslatedExpression();
213: this .getExpression().appendToTranslatedExpression(
214: this .sortedByClause);
215: }
216:
217: // remove any extra space from parenthesis
218: this .getExpression().replaceInTranslatedExpression("\\(\\s*",
219: "(");
220: this .getExpression().replaceInTranslatedExpression("\\s*\\)",
221: ")");
222: }
223:
224: /*------------------------- Handler methods ---------------------------------------*/
225:
226: /*------------------------- PropertyCallExpression Handler methods ---------------------*/
227:
228: public void handleSubSelect(String translation, Object node) {
229: APropertyCallExpression propertyCallExpression = (APropertyCallExpression) node;
230:
231: String primaryExpression = ConcreteSyntaxUtils
232: .getPrimaryExpression(propertyCallExpression);
233:
234: // set the association which the 'includesAll' indicates (which
235: // is the first feature call from the list of feature calls)
236: translation = this .replaceFragment(translation,
237: TranslationUtils.trimToEmpty(primaryExpression), 0);
238:
239: this .selectClause.append(" ");
240: this .selectClause.append(translation);
241: }
242:
243: public void handleIsLike(String translation, Object node) {
244: APropertyCallExpression propertyCallExpression = (APropertyCallExpression) node;
245: List featureCalls = ConcreteSyntaxUtils
246: .getFeatureCalls(propertyCallExpression);
247:
248: List params = params = ConcreteSyntaxUtils
249: .getParameters((AFeatureCall) featureCalls.get(0));
250:
251: translation = this .replaceFragment(translation,
252: TranslationUtils.deleteWhitespace(params.get(0)), 0);
253: translation = this .replaceFragment(translation,
254: TranslationUtils.deleteWhitespace(params.get(1)), 1);
255:
256: if (StringUtils.isNotEmpty(this .getExpression()
257: .getTranslatedExpression())) {
258: this .getExpression().appendSpaceToTranslatedExpression();
259: }
260: this .getExpression().appendToTranslatedExpression(translation);
261: }
262:
263: public void handleSelect(String translation, Object node) {
264: this .selectClause.append(translation);
265: }
266:
267: public void handleIncludes(String translation, Object node) {
268: APropertyCallExpression propertyCallExpression = (APropertyCallExpression) node;
269: List featureCalls = ConcreteSyntaxUtils
270: .getFeatureCalls(propertyCallExpression);
271:
272: // since the parameters can only be either dotFeatureCall or
273: // arrowFeatureCall we try one or the other.
274: String parameters = StringUtils
275: .deleteWhitespace(ConcreteSyntaxUtils
276: .getParametersAsString((AFeatureCall) featureCalls
277: .get(0)));
278:
279: String primaryExpression = ConcreteSyntaxUtils
280: .getPrimaryExpression(propertyCallExpression);
281:
282: translation = this .replaceFragment(translation,
283: primaryExpression, 1);
284: translation = this .replaceFragment(translation, parameters, 0);
285:
286: this .getExpression().appendSpaceToTranslatedExpression();
287: this .getExpression().appendToTranslatedExpression(translation);
288: }
289:
290: public void handleDotOperation(String translation, Object node) {
291: APropertyCallExpression propertyCallExpression = (APropertyCallExpression) node;
292: String firstArgument = ConcreteSyntaxUtils
293: .getPrimaryExpression(propertyCallExpression);
294: translation = this .replaceFragment(translation, firstArgument,
295: 0);
296: List featureCalls = ConcreteSyntaxUtils
297: .getFeatureCalls(propertyCallExpression);
298: if (featureCalls != null && !featureCalls.isEmpty()) {
299: // here we loop through the feature calls and find the ones
300: // that are operation feature calls, we then retrieve and replace
301: // all parameters in the translated expression
302: for (final Iterator callIterator = featureCalls.iterator(); callIterator
303: .hasNext();) {
304: AFeatureCall featureCall = (AFeatureCall) callIterator
305: .next();
306:
307: if (TranslationUtils.trimToEmpty(featureCall).matches(
308: OCLPatterns.OPERATION_FEATURE_CALL)) {
309: List parameters = ConcreteSyntaxUtils
310: .getParameters(featureCall);
311: if (parameters != null && !parameters.isEmpty()) {
312: Iterator parameterIterator = parameters
313: .iterator();
314: for (int ctr = 1; parameterIterator.hasNext(); ctr++) {
315: translation = this .replaceFragment(
316: translation,
317: (String) parameterIterator.next(),
318: ctr);
319: }
320: }
321: break;
322: }
323: }
324: }
325: this .getExpression().appendSpaceToTranslatedExpression();
326: this .getExpression().appendToTranslatedExpression(translation);
327: }
328:
329: private StringBuffer sortedByClause;
330:
331: public void handleSortedBy(String translation, Object node) {
332: if (this .sortedByClause.length() > 0) {
333: this .sortedByClause.append(", ");
334: }
335: this .sortedByClause.append(TranslationUtils
336: .deleteWhitespace(ConcreteSyntaxUtils
337: .getParametersAsString((AFeatureCall) node)));
338: }
339:
340: /*------------------------- Logical Expression Handler (and, or, xor, etc.) ----------------------*/
341:
342: public void handleLogicalExpression(String translation, Object node) {
343: this .getExpression().appendSpaceToTranslatedExpression();
344: this .getExpression().appendToTranslatedExpression(translation);
345: }
346:
347: /*------------------------- Relational Expression Handler (=, <, >, >=, etc.) --------------------*/
348:
349: public void handleRelationalExpression(String translation,
350: Object node) {
351: ARelationalExpressionTail relationalExpressionTail = (ARelationalExpressionTail) node;
352:
353: String[] leftAndRightExpressions = ConcreteSyntaxUtils
354: .getLeftAndRightExpressions((PRelationalExpression) relationalExpressionTail
355: .parent());
356: String leftExpression = StringUtils
357: .deleteWhitespace(leftAndRightExpressions[0]);
358: String rightExpression = StringUtils
359: .deleteWhitespace(leftAndRightExpressions[1]);
360: if (leftExpression.matches(OCLPatterns.OPERATION_FEATURE_CALL)) {
361: leftExpression = "";
362: }
363: translation = this .replaceFragment(translation, leftExpression,
364: 0);
365: if (rightExpression.matches(OCLPatterns.OPERATION_FEATURE_CALL)) {
366: rightExpression = "";
367: }
368: translation = this .replaceFragment(translation,
369: rightExpression, 1);
370:
371: this.getExpression().appendSpaceToTranslatedExpression();
372: this.getExpression().appendToTranslatedExpression(translation);
373: }
374: }
|