View Javadoc

1   /***
2    * Cecilia ADL Compiler
3    * Copyright (C) 2006-2007 STMicroelectronics
4    *
5    * This library is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU Lesser General Public
7    * License as published by the Free Software Foundation; either
8    * version 2 of the License, or (at your option) any later version.
9    *
10   * This library is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   * Lesser General Public License for more details.
14   *
15   * You should have received a copy of the GNU Lesser General Public
16   * License along with this library; if not, write to the Free Software
17   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   *
19   * Contact: fractal@objectweb.org
20   *
21   * Author:Ali Erdem Ozcan
22   */
23  
24  package org.objectweb.fractal.cecilia.primitive.thinkMC.source;
25  
26  import static org.objectweb.fractal.adl.NodeUtil.castNodeError;
27  import static org.objectweb.fractal.api.type.TypeFactory.OPTIONAL;
28  import static org.objectweb.fractal.cecilia.adl.SourceCodeHelper.appendSortedSourceCodes;
29  import static org.objectweb.fractal.cecilia.adl.implementations.ImplementationDecorationUtil.getCode;
30  
31  import java.io.File;
32  import java.io.IOException;
33  import java.util.ArrayList;
34  import java.util.Collection;
35  import java.util.HashMap;
36  import java.util.HashSet;
37  import java.util.List;
38  import java.util.Map;
39  import java.util.Set;
40  
41  import org.objectweb.fractal.adl.ADLException;
42  import org.objectweb.fractal.adl.CompilerError;
43  import org.objectweb.fractal.adl.ComponentVisitor;
44  import org.objectweb.fractal.adl.Node;
45  import org.objectweb.fractal.adl.components.ComponentContainer;
46  import org.objectweb.fractal.adl.error.GenericErrors;
47  import org.objectweb.fractal.adl.implementations.Implementation;
48  import org.objectweb.fractal.adl.implementations.ImplementationContainer;
49  import org.objectweb.fractal.api.Component;
50  import org.objectweb.fractal.cecilia.adl.SourceCodeProvider;
51  import org.objectweb.fractal.cecilia.adl.TypeNameProvider;
52  import org.objectweb.fractal.cecilia.adl.directives.Include;
53  import org.objectweb.fractal.cecilia.adl.directives.IncludeContainer;
54  import org.objectweb.fractal.cecilia.adl.file.CodeWriter;
55  import org.objectweb.fractal.cecilia.adl.file.SourceFile;
56  import org.objectweb.fractal.cecilia.adl.file.SourceFileProvider;
57  import org.objectweb.fractal.cecilia.adl.file.SourceFileWriter;
58  import org.objectweb.fractal.task.core.AbstractTaskFactoryUser;
59  import org.objectweb.fractal.task.core.Executable;
60  import org.objectweb.fractal.task.core.TaskException;
61  import org.objectweb.fractal.task.core.TaskFactory;
62  import org.objectweb.fractal.task.core.primitive.annotations.ClientInterface;
63  import org.objectweb.fractal.task.core.primitive.annotations.ServerInterface;
64  import org.objectweb.fractal.task.core.primitive.annotations.ServerInterfaces;
65  import org.objectweb.fractal.task.core.primitive.annotations.TaskParameters;
66  
67  /**
68   * Visitor component that builds the source file containing the components'
69   * definition and instance code.
70   */
71  public class SourceFileVisitor extends AbstractTaskFactoryUser
72      implements
73        ComponentVisitor {
74  
75    // ---------------------------------------------------------------------------
76    // Implementation of the ComponentVisitor interface
77    // ---------------------------------------------------------------------------
78  
79    /**
80     * Visits a {@link ComponentContainer} node and creates a task that creates
81     * the source file containing the components' definition and instance code.
82     */
83    public Component visit(final List<Node> path,
84        final ComponentContainer container, final Map<Object, Object> context)
85        throws ADLException, TaskException {
86      final Implementation impl = castNodeError(container,
87          ImplementationContainer.class).getImplementation();
88      if (impl == null) {
89        throw new CompilerError(GenericErrors.INTERNAL_ERROR,
90            "This visitor is only applicable for primitive component.");
91      }
92      final File adlBuildDirectory = (File) context.get("adlBuildDirectory");
93      final Component implTask = createSourceFileTask(container, impl,
94          adlBuildDirectory);
95      if (impl instanceof IncludeContainer
96          && ((IncludeContainer) impl).getIncludes().length != 0) {
97        final Include[] includes = ((IncludeContainer) impl).getIncludes();
98  
99        final Collection<Component> tasks = new ArrayList<Component>(
100           includes.length + 1);
101       tasks.add(implTask);
102 
103       final Set<String> moduleNames = new HashSet<String>();
104       for (final Include include : includes) {
105         // if the module is an assembly, file, it should not be included.
106         if (!((SourceFile) getCode((Node) include)).isAssemblyFile()) {
107           // find a unique module name.
108           String moduleName = include.getFile();
109 
110           // remove package name (if any)
111           final int i = moduleName.lastIndexOf('.');
112           if (i != -1) {
113             moduleName = moduleName.substring(i);
114           }
115 
116           if (!moduleNames.add(moduleName)) {
117             // a module with the same name already exist, append a number as
118             // suffix.
119             int suffix = 1;
120             // increment the suffix until a new name is found.
121             while (!moduleNames.add(moduleName + suffix))
122               suffix++;
123 
124             moduleName = moduleName + suffix;
125           }
126 
127           tasks.add(createModuleSourceFileTask(container, include, moduleName,
128               adlBuildDirectory));
129         }
130       }
131       return taskFactoryItf.newCompositeTask(tasks, TaskFactory.EXPORT_ALL,
132           null);
133     } else {
134       return implTask;
135     }
136   }
137 
138   // ---------------------------------------------------------------------------
139   // Utility methods
140   // ---------------------------------------------------------------------------
141 
142   protected Component createModuleSourceFileTask(
143       final ComponentContainer container, final Include include,
144       final String moduleName, final File adlBuildDirectory)
145       throws TaskException {
146     return taskFactoryItf.newPrimitiveTask(new ModuleSourceFileTask(
147         adlBuildDirectory, moduleName), container, include);
148   }
149 
150   protected Component createSourceFileTask(final ComponentContainer container,
151       final Implementation impl, final File adlBuildDirectory)
152       throws TaskException {
153     return taskFactoryItf.newPrimitiveTask(
154         new SourceFileTask(adlBuildDirectory), container);
155   }
156 
157   // ---------------------------------------------------------------------------
158   // Task classes
159   // ---------------------------------------------------------------------------
160 
161   protected abstract static class AbstractSourceFileTask
162       implements
163         Executable,
164         SourceFileProvider {
165     protected final File      adlBuildDirectory;
166 
167     // Produced source file
168     protected SourceFile      sourceFile;
169 
170     // -------------------------------------------------------------------------
171     // Task client interfaces
172     // -------------------------------------------------------------------------
173 
174     /** The name of the component type. */
175     @ClientInterface(name = "type-name-provider", record = "role:typeNameProvider, id:%", parameters = "componentNode")
176     public TypeNameProvider   typeNameProviderItf;
177 
178     /** Source code for the component definition. */
179     @ClientInterface(name = "component-definition-provider", record = "role:componentDefinition, id:%", parameters = "componentNode")
180     public SourceCodeProvider definitionProviderItf;
181 
182     /** Source code for the implementation code. */
183     @ClientInterface(name = "implementation-provider", record = "role:implementation, id:%", parameters = "componentNode")
184     public SourceCodeProvider implementationProviderItf;
185 
186     // -------------------------------------------------------------------------
187     // Task constructor
188     // -------------------------------------------------------------------------
189 
190     /**
191      * @param adlBuildDirectory The directory into which the generated file will
192      *          be placed.
193      */
194     public AbstractSourceFileTask(final File adlBuildDirectory) {
195       this.adlBuildDirectory = adlBuildDirectory;
196     }
197 
198     // -------------------------------------------------------------------------
199     // Abstract methods
200     // -------------------------------------------------------------------------
201 
202     protected abstract void prepareSourceCode(CodeWriter cw);
203 
204     protected abstract String getFileName();
205 
206     protected abstract String getSignature();
207 
208     // -------------------------------------------------------------------------
209     // Implementation of the Executable interface
210     // -------------------------------------------------------------------------
211 
212     public void execute() throws Exception {
213       final String fileName = getFileName();
214       final File outputFile = new File(adlBuildDirectory, fileName);
215 
216       final CodeWriter cw = new CodeWriter();
217       cw.appendln("// THIS FILE HAS BEEN GENERATED BY THE CECILIA ADL "
218           + "COMPILER.");
219       cw.appendln("// DO NOT EDIT").endl();
220       prepareSourceCode(cw);
221 
222       try {
223         SourceFileWriter.writeToFile(outputFile, cw.toString());
224       } catch (final IOException e) {
225         throw new ADLException(GenericErrors.INTERNAL_ERROR, e,
226             "An error occurs while writing to file '"
227                 + outputFile.getAbsolutePath() + "'");
228       }
229 
230       sourceFile = new SourceFile(getSignature(), outputFile);
231     }
232 
233     // -------------------------------------------------------------------------
234     // Implementation of the SourceFileProvider interface
235     // -------------------------------------------------------------------------
236 
237     public SourceFile getSourceFile() {
238       return sourceFile;
239     }
240   }
241 
242   /**
243    * Builds the ".adl.c" source file that contains the definition of the
244    * component structure, the inclusion of the implementation file and the
245    * instantiation/initialization of the static instances.
246    */
247   @TaskParameters("componentNode")
248   @ServerInterfaces(@ServerInterface(name = "component-definition-file-provider", signature = SourceFileProvider.class, record = "role:componentDefinitionFile, id:%", parameters = "componentNode"))
249   public static class SourceFileTask extends AbstractSourceFileTask {
250 
251     /** The suffix of the generated file. */
252     public static final String                   FILE_NAME_SUFFIX                              = ".adl.c";
253 
254     // -------------------------------------------------------------------------
255     // Task client interfaces
256     // -------------------------------------------------------------------------
257 
258     /** Source code for VFT declarations (optional). */
259     @ClientInterface(name = "vft-instantiation-provider", contingency = OPTIONAL, record = "role:interfaceVFTInstantiation, id:%", parameters = "componentNode")
260     public SourceCodeProvider                    vftInstantiationProviderItf;
261 
262     /**
263      * Collection interface to collect source code for the static component
264      * instances.
265      */
266     @ClientInterface(name = "component-instantiation-aggregator", signature = SourceCodeProvider.class, record = "role:componentInstantiationAggregator, id:%", parameters = "componentNode")
267     public final Map<String, SourceCodeProvider> instantiationProviderItf                      = new HashMap<String, SourceCodeProvider>();
268 
269     /**
270      * Client interface used to retrieve the additional implementation code.
271      * This interface can be used for generated implementation code (factory
272      * interface for instance).
273      */
274     @ClientInterface(name = "additional-implementation-source-code", signature = SourceCodeProvider.class, record = "role:additionalImplementationSourceCode, id:%", parameters = "componentNode")
275     public final Map<String, SourceCodeProvider> additionalImplementationSourceCodeProviderItf = new HashMap<String, SourceCodeProvider>();
276 
277     // -------------------------------------------------------------------------
278     // Task constructor
279     // -------------------------------------------------------------------------
280 
281     /**
282      * @param adlBuildDirectory The directory into which the generated file will
283      *          be placed.
284      */
285     public SourceFileTask(final File adlBuildDirectory) {
286       super(adlBuildDirectory);
287     }
288 
289     // -------------------------------------------------------------------------
290     // Implementation of abstract methods of AbstractImplementationTask
291     // -------------------------------------------------------------------------
292 
293     @Override
294     protected String getFileName() {
295       return typeNameProviderItf.getCTypeName() + FILE_NAME_SUFFIX;
296     }
297 
298     @Override
299     protected String getSignature() {
300       return typeNameProviderItf.getTypeName();
301     }
302 
303     @Override
304     protected void prepareSourceCode(final CodeWriter cw) {
305       // append source code that defines component type.
306       cw.append(definitionProviderItf.getSourceCode()).endl();
307 
308       // append implementation code.
309       cw.appendln(implementationProviderItf.getSourceCode()).endl();
310 
311       // append additional implementation code.
312       for (final SourceCodeProvider codeProvider : additionalImplementationSourceCodeProviderItf
313           .values()) {
314         cw.appendln(codeProvider.getSourceCode());
315       }
316 
317       // append source code that declare VTable of interfaces (if any)
318       if (vftInstantiationProviderItf != null)
319         cw.appendln(vftInstantiationProviderItf.getSourceCode());
320 
321       // append source codes that declare/initialize static instances.
322       // To ensure reproducible code generation, append controller definition
323       // codes in their alphabetic order.
324       appendSortedSourceCodes(cw, instantiationProviderItf.values());
325     }
326   }
327 
328   /**
329    * Builds the ".adl.c" source file that contains the definition of the
330    * component structure and the inclusion of a module of implementation.
331    */
332   @TaskParameters({"componentNode", "moduleNode"})
333   @ServerInterfaces(@ServerInterface(name = "component-module-file-provider", signature = SourceFileProvider.class, record = "role:componentModuleFile, id:%, module:%", parameters = {
334       "componentNode", "moduleNode"}))
335   public static class ModuleSourceFileTask extends AbstractSourceFileTask {
336 
337     /** The suffix of the generated file. */
338     public static final String FILE_NAME_SUFFIX = ".adl.c";
339 
340     protected final String     moduleName;
341 
342     // -------------------------------------------------------------------------
343     // Task client interfaces
344     // -------------------------------------------------------------------------
345 
346     /** Source code for module implementation. */
347     @ClientInterface(name = "module-implementation-provider", record = "role:implementation, id:%, module:%", parameters = {
348         "componentNode", "moduleNode"})
349     public SourceCodeProvider  moduleImplementationProviderItf;
350 
351     // -------------------------------------------------------------------------
352     // Task constructor
353     // -------------------------------------------------------------------------
354 
355     /**
356      * @param adlBuildDirectory The directory into which the generated file will
357      *          be placed.
358      * @param moduleName The name of the module to which this task correspond.
359      *          This name is used to generate a comprehensive file name.
360      */
361     public ModuleSourceFileTask(final File adlBuildDirectory,
362         final String moduleName) {
363       super(adlBuildDirectory);
364       this.moduleName = moduleName;
365     }
366 
367     // -------------------------------------------------------------------------
368     // Implementation of abstract methods of AbstractImplementationTask
369     // -------------------------------------------------------------------------
370 
371     @Override
372     protected String getFileName() {
373       return typeNameProviderItf.getCTypeName() + '_' + moduleName
374           + FILE_NAME_SUFFIX;
375     }
376 
377     @Override
378     protected String getSignature() {
379       return typeNameProviderItf.getTypeName() + '.' + moduleName;
380     }
381 
382     @Override
383     protected void prepareSourceCode(final CodeWriter cw) {
384       // append source code that defines component type.
385       cw.append(definitionProviderItf.getSourceCode()).endl();
386 
387       // append implementation code: in case of multi-module component, this
388       // source code defines only the private component data.
389       cw.appendln(implementationProviderItf.getSourceCode()).endl();
390 
391       // append module source code
392       cw.appendln(moduleImplementationProviderItf.getSourceCode());
393     }
394   }
395 }