001: package org.andromda.translation.ocl.testsuite;
002:
003: import org.andromda.core.common.ExceptionUtils;
004: import org.andromda.core.metafacade.ModelAccessFacade;
005: import org.andromda.core.translation.Expression;
006: import org.andromda.metafacades.uml.ClassifierFacade;
007: import org.andromda.metafacades.uml.ModelElementFacade;
008: import org.andromda.metafacades.uml.OperationFacade;
009: import org.andromda.metafacades.uml.ParameterFacade;
010: import org.andromda.translation.ocl.BaseTranslator;
011: import org.andromda.translation.ocl.node.AOperationContextDeclaration;
012: import org.andromda.translation.ocl.syntax.ConcreteSyntaxUtils;
013: import org.andromda.translation.ocl.syntax.OperationDeclaration;
014: import org.andromda.translation.ocl.syntax.VariableDeclaration;
015: import org.apache.commons.collections.CollectionUtils;
016: import org.apache.commons.collections.Predicate;
017: import org.apache.commons.lang.StringUtils;
018:
019: import java.util.ArrayList;
020: import java.util.Collection;
021: import java.util.Iterator;
022:
023: /**
024: * Finds the context element defined in the OCL expression.
025: *
026: * @author Chad Brandon
027: */
028: public class ContextElementFinder extends BaseTranslator {
029:
030: private ModelAccessFacade model;
031:
032: /**
033: * The Constructor which takes <code>model</code>, which is an instance of ModelAccessFacade that will allow us to
034: * search the model for the context element.
035: *
036: * @param model the ModelAccessFacade to search.
037: */
038: public ContextElementFinder(ModelAccessFacade model) {
039: ExceptionUtils.checkNull("model", model);
040: this .model = model;
041: }
042:
043: /**
044: * Hide the default constructor
045: */
046: private ContextElementFinder() {
047: }
048:
049: /**
050: * The operation that is set if the context of the constraint happens to be an operation.
051: */
052: protected OperationDeclaration operation = null;
053:
054: /**
055: * The found context type.
056: */
057: private Object contextElement = null;
058:
059: /**
060: * @see org.andromda.core.translation.analysis.DepthFirstAdapter#inAOperationContextDeclaration(org.andromda.core.translation.node.AOperationContextDeclaration)
061: */
062: public void inAOperationContextDeclaration(
063: AOperationContextDeclaration declaration) {
064: super .inAOperationContextDeclaration(declaration);
065: if (declaration != null) {
066: operation = ConcreteSyntaxUtils
067: .getOperationDeclaration(declaration.getOperation());
068: }
069: }
070:
071: /**
072: * We use the postProcess method to retrieve the contextType from the expression and then find the actual model
073: * element using metafacades.
074: *
075: * @see org.andromda.translation.ocl.BaseTranslator#postProcess()
076: */
077: public void postProcess() {
078: super .postProcess();
079: Expression expression = this .getExpression();
080: if (expression != null) {
081: String contextElementName = expression.getContextElement();
082: this .contextElement = this
083: .findModelElement(contextElementName.replaceAll(
084: "::", "\\."));
085: if (this .contextElement != null) {
086: logger.info("found context element --> '"
087: + contextElementName + "'");
088: } else {
089: logger.info("Could not find model element --> '"
090: + contextElementName + "'");
091: }
092:
093: if (this .contextElement != null
094: && this .operation != null
095: && ClassifierFacade.class
096: .isAssignableFrom(contextElement.getClass())) {
097: ClassifierFacade type = (ClassifierFacade) this .contextElement;
098: Collection operations = type.getOperations();
099: this .contextElement = CollectionUtils.find(operations,
100: new OperationFinder());
101: if (this .contextElement == null) {
102: throw new ContextElementFinderException(
103: "No operation matching '"
104: + operation
105: + "' could be found on element --> '"
106: + contextElementName
107: + "', please check your model");
108: }
109:
110: // if we only have one operation then we just set that
111: // as the context element, otherwise we'll need to figure
112: // out which operation is the context operation by checking
113: // the arguments.
114: if (operations.size() == 1) {
115: this .contextElement = operations.iterator().next();
116: } else {
117: // now find the correct operation since there are
118: // more than one with the same name
119: }
120: }
121: }
122: }
123:
124: private final class OperationFinder implements Predicate {
125: public boolean evaluate(Object object) {
126:
127: OperationFacade facadeOperation = (OperationFacade) object;
128: boolean valid = StringUtils.trimToEmpty(
129: facadeOperation.getName()).equals(
130: StringUtils.trimToEmpty(operation.getName()));
131: // if we've found an operation with a matching name
132: // check the parameters
133: if (valid) {
134: valid = argumentsMatch(operation, facadeOperation);
135: }
136: return valid;
137: }
138: }
139:
140: /**
141: * Returns true if the arguments contained within <code>oclOperation</code> and <code>facadeOperation</code> match,
142: * false otherwise.
143: *
144: * @param oclOperation an OCL Operation
145: * @param facadeOperation a metafacade Operation
146: * @return boolean whether the arguments match.
147: */
148: protected boolean argumentsMatch(OperationDeclaration oclOperation,
149: OperationFacade facadeOperation) {
150: boolean argumentsMatch = this .argumentCountsMatch(oclOperation,
151: facadeOperation);
152: if (argumentsMatch) {
153: argumentsMatch = this .argumentNamesMatch(oclOperation,
154: facadeOperation);
155: }
156: return argumentsMatch;
157: }
158:
159: /**
160: * Returns true if the number of arguments contained within <code>oclOperation</code> and
161: * <code>facadeOperation</code> match, false otherwise.
162: *
163: * @param oclOperation an OCL Operation
164: * @param facadeOperation a metafacade Operation
165: * @return boolean whether the count of the arguments match.
166: */
167: private boolean argumentCountsMatch(
168: OperationDeclaration oclOperation,
169: OperationFacade facadeOperation) {
170: ExceptionUtils.checkNull("oclOperation", oclOperation);
171: ExceptionUtils.checkNull("facadeOperation", facadeOperation);
172: VariableDeclaration[] expressionOpArgs = oclOperation
173: .getArguments();
174: Collection facadeOpArgs = facadeOperation.getArguments();
175: boolean countsMatch = (expressionOpArgs == null || expressionOpArgs.length == 0)
176: && (facadeOpArgs == null || facadeOpArgs.isEmpty());
177: if (!countsMatch) {
178: countsMatch = expressionOpArgs != null
179: && facadeOpArgs != null
180: && expressionOpArgs.length == facadeOpArgs.size();
181: }
182: return countsMatch;
183: }
184:
185: /**
186: * Returns true if the argument names contained within <code>oclOperation</code> and <code>facadeOperation</code>
187: * match, false otherwise.
188: *
189: * @param oclOperation an OCL Operation
190: * @param facadeOperation a metafacade Operation
191: * @return boolean whether the arg names match or not.
192: */
193: private boolean argumentNamesMatch(
194: OperationDeclaration oclOperation,
195: OperationFacade facadeOperation) {
196: ExceptionUtils.checkNull("oclOperation", oclOperation);
197: ExceptionUtils.checkNull("facadeOperation", facadeOperation);
198:
199: Collection facadeOpArguments = facadeOperation.getArguments();
200: VariableDeclaration[] expressionOpArgs = oclOperation
201: .getArguments();
202: Collection expressionArgNames = new ArrayList();
203: if (expressionOpArgs != null) {
204: for (int ctr = 0; ctr < expressionOpArgs.length; ctr++) {
205: expressionArgNames.add(expressionOpArgs[ctr].getName());
206: }
207: }
208: Collection facadeArgNames = new ArrayList();
209: if (facadeOpArguments != null) {
210: Iterator facadeOpArgumentIt = facadeOpArguments.iterator();
211: while (facadeOpArgumentIt.hasNext()) {
212: ParameterFacade facadeArg = (ParameterFacade) facadeOpArgumentIt
213: .next();
214: facadeArgNames.add(facadeArg.getName());
215: }
216: }
217: return CollectionUtils.isEqualCollection(expressionArgNames,
218: facadeArgNames);
219: }
220:
221: /**
222: * Finds the model element with the given <code>modelElementName</code>. Will find either the non qualified name or
223: * qualified name. If more than one model element is found with the non qualified name an exception will be thrown.
224: *
225: * @param modelElementName
226: * @return Object the found model element
227: */
228: private Object findModelElement(final String modelElementName) {
229: Object modelElement = null;
230: Collection modelElements = this .model.getModelElements();
231: CollectionUtils.filter(modelElements, new Predicate() {
232: public boolean evaluate(Object object) {
233: boolean valid = false;
234: if (ModelElementFacade.class.isAssignableFrom(object
235: .getClass())) {
236: ModelElementFacade modelElement = (ModelElementFacade) object;
237: String elementName = StringUtils
238: .trimToEmpty(modelElement.getName());
239: String name = StringUtils
240: .trimToEmpty(modelElementName);
241: valid = elementName.equals(name);
242: if (!valid) {
243: elementName = StringUtils
244: .trimToEmpty(modelElement
245: .getFullyQualifiedName());
246: valid = elementName.equals(name);
247: }
248: }
249: return valid;
250: }
251: });
252: if (modelElements.size() > 1) {
253: throw new ContextElementFinderException(
254: "More than one element named '" + modelElementName
255: + "' was found within your model,"
256: + " please give the fully qualified name");
257: } else if (modelElements.size() == 1) {
258: modelElement = modelElements.iterator().next();
259: }
260: return modelElement;
261: }
262:
263: /**
264: * Returns the context element found in the model from the expression.
265: *
266: * @return the context type as a ModelElementFacade.
267: */
268: public Object getContextElement() {
269: return this.contextElement;
270: }
271:
272: }
|