View Javadoc
1   package org.andromda.translation.ocl.testsuite;
2   
3   import java.util.ArrayList;
4   import java.util.Collection;
5   import org.andromda.core.common.ExceptionUtils;
6   import org.andromda.core.metafacade.ModelAccessFacade;
7   import org.andromda.core.translation.Expression;
8   import org.andromda.metafacades.uml.ClassifierFacade;
9   import org.andromda.metafacades.uml.ModelElementFacade;
10  import org.andromda.metafacades.uml.OperationFacade;
11  import org.andromda.metafacades.uml.ParameterFacade;
12  import org.andromda.translation.ocl.BaseTranslator;
13  import org.andromda.translation.ocl.node.AOperationContextDeclaration;
14  import org.andromda.translation.ocl.node.POperation;
15  import org.andromda.translation.ocl.syntax.ConcreteSyntaxUtils;
16  import org.andromda.translation.ocl.syntax.OperationDeclaration;
17  import org.andromda.translation.ocl.syntax.VariableDeclaration;
18  import org.apache.commons.collections.CollectionUtils;
19  import org.apache.commons.collections.Predicate;
20  import org.apache.commons.lang.StringUtils;
21  
22  /**
23   * Finds the context element defined in the OCL expression.
24   *
25   * @author Chad Brandon
26   */
27  public class ContextElementFinder
28          extends BaseTranslator
29  {
30      private ModelAccessFacade model;
31  
32      /**
33       * The Constructor which takes <code>model</code>, which is an instance of ModelAccessFacade that will allow us to
34       * search the model for the context element.
35       *
36       * @param model the ModelAccessFacade to search.
37       */
38      public ContextElementFinder(ModelAccessFacade model)
39      {
40          ExceptionUtils.checkNull("model", model);
41          this.model = model;
42      }
43  
44      /**
45       * Hide the default constructor
46       */
47      @SuppressWarnings("unused")
48      private ContextElementFinder()
49      {
50      }
51  
52      /**
53       * The operation that is set if the context of the constraint happens to be an operation.
54       */
55      protected OperationDeclaration operation = null;
56  
57      /**
58       * The found context type.
59       */
60      private Object contextElement = null;
61  
62      /**
63       * @param declaration
64       * @see org.andromda.translation.ocl.syntax.ConcreteSyntaxUtils#getOperationDeclaration(POperation)
65       */
66      public void inAOperationContextDeclaration(AOperationContextDeclaration declaration)
67      {
68          super.inAOperationContextDeclaration(declaration);
69          if (declaration != null)
70          {
71              operation = ConcreteSyntaxUtils.getOperationDeclaration(declaration.getOperation());
72          }
73      }
74  
75      /**
76       * We use the postProcess method to retrieve the contextType from the expression and then find the actual model
77       * element using metafacades.
78       *
79       * @see org.andromda.translation.ocl.BaseTranslator#postProcess()
80       */
81      @Override
82      public void postProcess()
83      {
84          Expression expression = this.getExpression();
85          if (expression != null)
86          {
87              String contextElementName = expression.getContextElement();
88              this.contextElement = this.findModelElement(contextElementName.replaceAll("::", "\\."));
89              if (this.contextElement != null)
90              {
91                  logger.info("found context element --> '" + contextElementName + '\'');
92              } else
93              {
94                  logger.info("Could not find model element --> '" + contextElementName + '\'');
95              }
96  
97              if (this.contextElement != null && this.operation != null &&
98                      ClassifierFacade.class.isAssignableFrom(contextElement.getClass()))
99              {
100                 ClassifierFacade type = (ClassifierFacade) this.contextElement;
101                 Collection operations = type.getOperations();
102                 this.contextElement = CollectionUtils.find(operations, new OperationFinder());
103                 if (this.contextElement == null)
104                 {
105                     throw new ContextElementFinderException("No operation matching '" + operation +
106                             "' could be found on element --> '" + contextElementName + "', please check your model");
107                 }
108 
109                 // if we only have one operation then we just set that
110                 // as the context element, otherwise we'll need to figure
111                 // out which operation is the context operation by checking
112                 // the arguments.
113                 if (operations.size() == 1)
114                 {
115                     this.contextElement = operations.iterator().next();
116                 } else
117                 {
118                     // now find the correct operation since there are
119                     // more than one with the same name
120                 }
121             }
122         }
123     }
124 
125     private final class OperationFinder
126             implements Predicate
127     {
128         public boolean evaluate(Object object)
129         {
130 
131             OperationFacade facadeOperation = (OperationFacade) object;
132             boolean valid = StringUtils.trimToEmpty(facadeOperation.getName()).equals(
133                     StringUtils.trimToEmpty(operation.getName()));
134             // if we've found an operation with a matching name
135             // check the parameters
136             if (valid)
137             {
138                 valid = argumentsMatch(operation, facadeOperation);
139             }
140             return valid;
141         }
142     }
143 
144     /**
145      * Returns true if the arguments contained within <code>oclOperation</code> and <code>facadeOperation</code> match,
146      * false otherwise.
147      *
148      * @param oclOperation    an OCL Operation
149      * @param facadeOperation a metafacade Operation
150      * @return boolean whether the arguments match.
151      */
152     protected boolean argumentsMatch(OperationDeclaration oclOperation, OperationFacade facadeOperation)
153     {
154         boolean argumentsMatch = this.argumentCountsMatch(oclOperation, facadeOperation);
155         if (argumentsMatch)
156         {
157             argumentsMatch = this.argumentNamesMatch(oclOperation, facadeOperation);
158         }
159         return argumentsMatch;
160     }
161 
162     /**
163      * Returns true if the number of arguments contained within <code>oclOperation</code> and
164      * <code>facadeOperation</code> match, false otherwise.
165      *
166      * @param oclOperation    an OCL Operation
167      * @param facadeOperation a metafacade Operation
168      * @return boolean whether the count of the arguments match.
169      */
170     private boolean argumentCountsMatch(OperationDeclaration oclOperation, OperationFacade facadeOperation)
171     {
172         ExceptionUtils.checkNull("oclOperation", oclOperation);
173         ExceptionUtils.checkNull("facadeOperation", facadeOperation);
174         VariableDeclaration[] expressionOpArgs = oclOperation.getArguments();
175         Collection facadeOpArgs = facadeOperation.getArguments();
176         boolean countsMatch = (expressionOpArgs == null || expressionOpArgs.length == 0) &&
177                 (facadeOpArgs == null || facadeOpArgs.isEmpty());
178         if (!countsMatch)
179         {
180             countsMatch = expressionOpArgs != null && facadeOpArgs != null &&
181                     expressionOpArgs.length == facadeOpArgs.size();
182         }
183         return countsMatch;
184     }
185 
186     /**
187      * Returns true if the argument names contained within <code>oclOperation</code> and <code>facadeOperation</code>
188      * match, false otherwise.
189      *
190      * @param oclOperation    an OCL Operation
191      * @param facadeOperation a metafacade Operation
192      * @return boolean whether the arg names match or not.
193      */
194     private boolean argumentNamesMatch(OperationDeclaration oclOperation, OperationFacade facadeOperation)
195     {
196         ExceptionUtils.checkNull("oclOperation", oclOperation);
197         ExceptionUtils.checkNull("facadeOperation", facadeOperation);
198 
199         Collection<ParameterFacade> facadeOpArguments = facadeOperation.getArguments();
200         VariableDeclaration[] expressionOpArgs = oclOperation.getArguments();
201         Collection<String> expressionArgNames = new ArrayList<String>();
202         if (expressionOpArgs != null)
203         {
204             for (VariableDeclaration expressionOpArg : expressionOpArgs)
205             {
206                 expressionArgNames.add(expressionOpArg.getName());
207             }
208         }
209         Collection<String> facadeArgNames = new ArrayList<String>();
210         if (facadeOpArguments != null)
211         {
212             for (ParameterFacade facadeArg : facadeOpArguments)
213             {
214                 facadeArgNames.add(facadeArg.getName());
215             }
216         }
217         return CollectionUtils.isEqualCollection(expressionArgNames, facadeArgNames);
218     }
219 
220     /**
221      * Finds the model element with the given <code>modelElementName</code>. Will find either the non qualified name or
222      * qualified name. If more than one model element is found with the non qualified name an exception will be thrown.
223      *
224      * @param modelElementName
225      * @return Object the found model element
226      */
227     private Object findModelElement(final String modelElementName)
228     {
229         Object modelElement = null;
230         Collection modelElements = this.model.getModelElements();
231         CollectionUtils.filter(modelElements, new Predicate()
232         {
233             public boolean evaluate(Object object)
234             {
235                 boolean valid = false;
236                 if (ModelElementFacade.class.isAssignableFrom(object.getClass()))
237                 {
238                     ModelElementFacade modelElement = (ModelElementFacade) object;
239                     String elementName = StringUtils.trimToEmpty(modelElement.getName());
240                     String name = StringUtils.trimToEmpty(modelElementName);
241                     valid = elementName.equals(name);
242                     if (!valid)
243                     {
244                         elementName = StringUtils.trimToEmpty(modelElement.getFullyQualifiedName());
245                         valid = elementName.equals(name);
246                     }
247                 }
248                 return valid;
249             }
250         });
251         if (modelElements.size() > 1)
252         {
253             throw new ContextElementFinderException("More than one element named '" + modelElementName +
254                     "' was found within your model," + " please give the fully qualified name");
255         } else if (modelElements.size() == 1)
256         {
257             modelElement = modelElements.iterator().next();
258         }
259         return modelElement;
260     }
261 
262     /**
263      * Returns the context element found in the model from the expression.
264      *
265      * @return the context type as a ModelElementFacade.
266      */
267     public Object getContextElement()
268     {
269         return this.contextElement;
270     }
271 
272 }