View Javadoc
1   package org.andromda.translation.ocl.parser;
2   
3   import java.util.ArrayList;
4   import java.util.HashMap;
5   import java.util.Iterator;
6   import java.util.LinkedList;
7   import java.util.List;
8   import java.util.Map;
9   import org.andromda.translation.ocl.analysis.AnalysisAdapter;
10  import org.andromda.translation.ocl.analysis.DepthFirstAdapter;
11  import org.andromda.translation.ocl.lexer.Lexer;
12  import org.andromda.translation.ocl.node.AActualParameterList;
13  import org.andromda.translation.ocl.node.ABarFeatureCallParameterOption;
14  import org.andromda.translation.ocl.node.AColonFeatureCallParameterOption;
15  import org.andromda.translation.ocl.node.ACommaExpression;
16  import org.andromda.translation.ocl.node.ACommaFeatureCallParameterOption;
17  import org.andromda.translation.ocl.node.AConcreteFeatureCallParameters;
18  import org.andromda.translation.ocl.node.AEqualExpression;
19  import org.andromda.translation.ocl.node.AFeatureCallParameters;
20  import org.andromda.translation.ocl.node.AIterateDeclarator;
21  import org.andromda.translation.ocl.node.AIterateFeatureCallParameterOption;
22  import org.andromda.translation.ocl.node.APathName;
23  import org.andromda.translation.ocl.node.AStandardDeclarator;
24  import org.andromda.translation.ocl.node.ATypeDeclaration;
25  import org.andromda.translation.ocl.node.AVariableDeclaration;
26  import org.andromda.translation.ocl.node.AVariableDeclarationList;
27  import org.andromda.translation.ocl.node.AVariableDeclarationListTail;
28  import org.andromda.translation.ocl.node.Node;
29  import org.andromda.translation.ocl.node.PExpression;
30  import org.andromda.translation.ocl.node.PFeatureCallParameterOption;
31  import org.andromda.translation.ocl.node.TName;
32  
33  /**
34   * This class adapts the Parser class to handle expressions in which the SableCC parser can't handle.
35   */
36  public class OclParser
37          extends Parser
38  {
39      /**
40       *
41       */
42      protected Node oclNode;
43  
44      /**
45       * Constructs an instance of OclParser.
46       *
47       * @param lexer
48       */
49      public OclParser(Lexer lexer)
50      {
51          super(lexer);
52      }
53  
54      /**
55       * @see org.andromda.translation.ocl.parser.Parser#filter()
56       */
57      protected void filter()
58      {
59          oclNode = node;
60          oclNode.apply(handler);
61          node = oclNode;
62      }
63  
64      /**
65       *
66       */
67      protected SyntaxHandler handler = new SyntaxHandler();
68  
69      /**
70       * A private inner class for handling syntax which SableCC can't handle on its own.
71       */
72      private class SyntaxHandler
73              extends AnalysisAdapter
74      {
75  
76          /**
77           * @param featureCallParameters
78           * @see #org.andromda.translation.ocl.parser.OclParser.SyntaxHandler.getParametersWithStandardDeclarator(AConcreteFeatureCallParameters featureCallParameters, PFeatureCallParameterOption[] parameterOptions)
79           */
80          public void caseAConcreteFeatureCallParameters(AConcreteFeatureCallParameters featureCallParameters)
81          {
82              boolean isDeclarator = false;
83              boolean isIterateDeclarator = false;
84  
85              List tail = featureCallParameters.getFeatureCallParameterOption();
86              PFeatureCallParameterOption[] parameterOption = new PFeatureCallParameterOption[tail.size()];
87              Iterator iter = tail.iterator();
88  
89              for (int ctr = 0; iter.hasNext(); ctr++)
90              {
91                  PFeatureCallParameterOption option = (PFeatureCallParameterOption) iter.next();
92                  parameterOption[ctr] = option;
93                  isIterateDeclarator = option instanceof AIterateFeatureCallParameterOption;
94                  if (!isIterateDeclarator)
95                  {
96                      isDeclarator = option instanceof ABarFeatureCallParameterOption;
97                  }
98              }
99  
100             if (isIterateDeclarator && !isDeclarator)
101             {
102                 throw new OclParserException("Parser Error: Illegal feature call parameters format in \"" +
103                         featureCallParameters + "\"; " + "must contain \";\" only if it contains \"|\"");
104             }
105             AFeatureCallParameters parameters;
106             if (isIterateDeclarator)
107             {
108                 parameters = getParametersWithIterateDeclarator(featureCallParameters,
109                         featureCallParameters.getExpression(), parameterOption);
110             }
111             else if (isDeclarator)
112             {
113                 parameters = getParametersWithStandardDeclarator(featureCallParameters, parameterOption);
114             }
115             else
116             {
117                 parameters = getParametersWithoutDeclarator(featureCallParameters,
118                         featureCallParameters.getExpression(), parameterOption);
119             }
120             oclNode = parameters;
121         }
122 
123         /**
124          * Gets the AFeatureCallParameters with a iterate declarator.
125          *
126          * @param featureCallParameters
127          * @param expression
128          * @param parameterOption
129          * @return AFeatureCallParameters
130          */
131         protected AFeatureCallParameters getParametersWithIterateDeclarator(
132                 AConcreteFeatureCallParameters featureCallParameters, PExpression expression,
133                 PFeatureCallParameterOption[] parameterOption)
134         {
135             AIterateDeclarator iteratorDeclarator = new AIterateDeclarator();
136 
137             AColonFeatureCallParameterOption featureCallParameterOption0 = (AColonFeatureCallParameterOption) parameterOption[0];
138             AIterateFeatureCallParameterOption featureCallParameterOption1 = (AIterateFeatureCallParameterOption) parameterOption[1];
139             ABarFeatureCallParameterOption featureCallParameterOption2 = (ABarFeatureCallParameterOption) parameterOption[2];
140 
141             AVariableDeclaration iterator = new AVariableDeclaration(getName(expression),
142                     featureCallParameterOption0.getTypeDeclaration());
143             iteratorDeclarator.setIterator(iterator);
144             iteratorDeclarator.setSemicolon(featureCallParameterOption1.getSemicolon());
145 
146             AVariableDeclaration accumulator = new AVariableDeclaration(featureCallParameterOption1.getName(),
147                     featureCallParameterOption1.getTypeDeclaration());
148             iteratorDeclarator.setAccumulator(accumulator);
149 
150             AEqualExpression equalExpression = new AEqualExpression(featureCallParameterOption1.getEqual(),
151                     featureCallParameterOption1.getExpression());
152             iteratorDeclarator.setEqualExpression(equalExpression);
153             iteratorDeclarator.setBar(featureCallParameterOption2.getBar());
154             AActualParameterList params = new AActualParameterList(featureCallParameterOption2.getExpression(),
155                     new ArrayList());
156             return new AFeatureCallParameters(featureCallParameters.getLParen(), iteratorDeclarator, params,
157                     featureCallParameters.getRParen());
158         }
159 
160         /**
161          * Gets AFeatureCallParameters from the standard declarator.
162          *
163          * @param featureCallParameters
164          * @param parameterOptions
165          * @return AFeatureCallParameters
166          */
167         protected AFeatureCallParameters getParametersWithStandardDeclarator(
168                 AConcreteFeatureCallParameters featureCallParameters, PFeatureCallParameterOption[] parameterOptions)
169         {
170 
171             int parameterOptionNum = parameterOptions.length;
172 
173             // check if the parameterOptions (after the first two)
174             // are instances of either ACommaFeatureCallParameter
175             // (so we can retrieve something like ', name') or
176             // ColonFeatureCallParameterOption (so we can retrieve
177             // something like ': type') and valid to false if this
178             // isn't the case.
179             for (int ctr = 0; ctr < parameterOptionNum - 2; ctr++)
180             {
181                 if (!(parameterOptions[ctr] instanceof ACommaFeatureCallParameterOption ||
182                         parameterOptions[ctr] instanceof AColonFeatureCallParameterOption))
183                 {
184                     throw new OclParserException("OCL Parser Error: Feature call parameters with " +
185                             "a standard declarator must have the format " +
186                             "\"( name (: type)?, ... , name (: type)? | expression )\"");
187                 }
188             }
189 
190             ABarFeatureCallParameterOption barParameterType = (ABarFeatureCallParameterOption) parameterOptions[parameterOptionNum -
191                     1];
192 
193             AStandardDeclarator standardDeclarator = new AStandardDeclarator(
194                     this.getVariableDeclarationList(featureCallParameters), barParameterType.getBar());
195             AActualParameterList params = new AActualParameterList(barParameterType.getExpression(), new ArrayList());
196             return new AFeatureCallParameters(featureCallParameters.getLParen(), standardDeclarator, params,
197                     featureCallParameters.getRParen());
198         }
199 
200         /**
201          * Gets the AFeatureCallParameter instance without the declarator.
202          *
203          * @param featureCallParameters
204          * @param expr
205          * @param parameterOption
206          * @return AFeatureCallParameters
207          */
208         protected AFeatureCallParameters getParametersWithoutDeclarator(
209                 AConcreteFeatureCallParameters featureCallParameters, PExpression expr,
210                 PFeatureCallParameterOption[] parameterOption)
211         {
212 
213             List paramList = new ArrayList();
214 
215             for (int ctr = 0; ctr < parameterOption.length; ctr++)
216             {
217                 if (!(parameterOption[ctr] instanceof ACommaFeatureCallParameterOption))
218                 {
219                     throw new OclParserException("parser error: declarator-less feature call paramaters must have the format " +
220                             "\"( expr, ..., expr )\"");
221                 }
222                 ACommaFeatureCallParameterOption commaOption = (ACommaFeatureCallParameterOption) parameterOption[ctr];
223                 ACommaExpression commaExpression = new ACommaExpression(commaOption.getComma(),
224                         commaOption.getExpression());
225                 paramList.add(commaExpression);
226             }
227 
228             return new AFeatureCallParameters(featureCallParameters.getLParen(), null,
229                     new AActualParameterList(expr, paramList), featureCallParameters.getRParen());
230         }
231 
232         /**
233          * Gets the AVariableDeclarationList instance from the <code>params</code> by apply the
234          * VariableDeclarationListFinder to it.
235          *
236          * @param params the params node to parse.
237          * @return the found AVariableDeclarationList instance.
238          */
239         protected AVariableDeclarationList getVariableDeclarationList(AConcreteFeatureCallParameters params)
240         {
241             VariableDeclarationListFinder finder = new VariableDeclarationListFinder();
242             params.apply(finder);
243             return finder.getList();
244         }
245     }
246 
247     /**
248      * A tree traversal class that searches for a name in a expression.
249      */
250     private class VariableDeclarationListFinder
251             extends DepthFirstAdapter
252     {
253         /**
254          * Stores the variable names in an ordered fashion so that we can retrieve them from the namesAndTypes map in
255          * the order they were stored.
256          */
257         private List orderedNames = new LinkedList();
258 
259         /**
260          * Stores the variable names along with its variable type (if there is one).
261          */
262         private Map namesAndTypes = new HashMap();
263 
264         /**
265          * @param name
266          * @see org.andromda.translation.ocl.parser.OclParser.VariableDeclarationListFinder#orderedNames
267          */
268         public void inAPathName(APathName name)
269         {
270             // we only want to add the first name (since the other
271             // names will all be comma separated and stored within
272             // the inACommaFeatureCallParameterOption() method)
273             if (this.namesAndTypes.isEmpty())
274             {
275                 TName initialVariableName = name.getName();
276                 this.orderedNames.add(initialVariableName);
277                 this.namesAndTypes.put(((LinkedList)this.orderedNames).getLast(), null);
278             }
279         }
280 
281         /**
282          * @param commaName
283          * @see org.andromda.translation.ocl.parser.OclParser.VariableDeclarationListFinder#orderedNames
284          */
285         public void inACommaFeatureCallParameterOption(ACommaFeatureCallParameterOption commaName)
286         {
287             this.orderedNames.add(commaName);
288             this.namesAndTypes.put(commaName, null);
289         }
290 
291         /**
292          * @param type
293          * @see org.andromda.translation.ocl.parser.OclParser.VariableDeclarationListFinder#namesAndTypes
294          */
295         public void inATypeDeclaration(ATypeDeclaration type)
296         {
297             if (this.namesAndTypes.containsKey(((LinkedList)this.orderedNames).getLast()))
298             {
299                 this.namesAndTypes.put(((LinkedList)this.orderedNames).getLast(), type);
300             }
301         }
302 
303         /**
304          * Extracts and constructs AVariableDeclarationlist from a AConcreteFeatureCallParameters instance.
305          *
306          * @return AVariableDeclarationList
307          */
308         public AVariableDeclarationList getList()
309         {
310 
311             TName initialName = (TName) ((LinkedList)this.orderedNames).getFirst();
312 
313             ATypeDeclaration typeDeclaration = (ATypeDeclaration) namesAndTypes.get(initialName);
314 
315             List variableDeclarationListTails = new ArrayList();
316             if (!this.orderedNames.isEmpty())
317             {
318                 int orderedNameSize = orderedNames.size();
319                 for (int ctr = 1; ctr < orderedNameSize; ctr++)
320                 {
321                     ACommaFeatureCallParameterOption name = (ACommaFeatureCallParameterOption) this.orderedNames.get(
322                             ctr);
323 
324                     ATypeDeclaration typeDecl = (ATypeDeclaration) this.namesAndTypes.get(name);
325 
326                     AVariableDeclaration variableDeclaration = new AVariableDeclaration(getName(name.getExpression()),
327                             typeDecl);
328 
329                     variableDeclarationListTails.add(new AVariableDeclarationListTail(name.getComma(),
330                             variableDeclaration, null));
331                 }
332             }
333 
334             AVariableDeclarationList list = new AVariableDeclarationList(
335                     new AVariableDeclaration(initialName, typeDeclaration), null, variableDeclarationListTails);
336             return list;
337         }
338     }
339 
340     /**
341      * A tree traversal class that searches for a name in a expression.
342      */
343     private class NameFinder
344             extends DepthFirstAdapter
345     {
346         private TName foundName;
347 
348         /**
349          * @param pathName
350          * @see org.andromda.translation.ocl.node.APathName#getName()
351          */
352         public void caseAPathName(APathName pathName)
353         {
354             this.foundName = pathName.getName();
355         }
356 
357         /**
358          * @return String the TName
359          */
360         public TName getName()
361         {
362             return this.foundName;
363         }
364     }
365 
366     /**
367      * Gets the TName from the <code>expression</code>.
368      *
369      * @param expression
370      * @return TName the name extracted from the <code>expression</code>.
371      */
372     protected TName getName(PExpression expression)
373     {
374         NameFinder finder = new NameFinder();
375         expression.apply(finder);
376         return finder.getName();
377     }
378 }