View Javadoc
1   package org.andromda.andromdapp;
2   
3   import java.io.BufferedReader;
4   import java.io.File;
5   import java.io.IOException;
6   import java.io.InputStreamReader;
7   import java.net.MalformedURLException;
8   import java.net.URL;
9   import java.util.ArrayList;
10  import java.util.Iterator;
11  import java.util.LinkedHashMap;
12  import java.util.List;
13  import java.util.Map;
14  import java.util.Set;
15  import org.andromda.core.common.AndroMDALogger;
16  import org.andromda.core.common.ResourceFinder;
17  import org.andromda.core.common.ResourceUtils;
18  import org.andromda.core.common.XmlObjectFactory;
19  import org.apache.commons.lang.StringUtils;
20  
21  /**
22   * Represents an instance of the AndroMDA application
23   * generator.
24   *
25   * @author Chad Brandon
26   */
27  public class AndroMDApp
28  {
29      /**
30       * An AndroMDApp configuration that contains some internal configuration information (like the AndroMDA
31       * version, and other common properties).
32       */
33      private static final String INTERNAL_CONFIGURATION_URI = "META-INF/andromdapp/configuration.xml";
34  
35      /**
36       * Runs the AndroMDApp generation process.
37       */
38      public void run()
39      {
40          try
41          {
42              AndroMDALogger.initialize();
43              final URL internalConfiguration = ResourceUtils.getResource(INTERNAL_CONFIGURATION_URI);
44              if (internalConfiguration == null)
45              {
46                  throw new AndroMDAppException("No configuration could be loaded from --> '" +
47                      INTERNAL_CONFIGURATION_URI + '\'');
48              }
49              this.addConfigurationUri(internalConfiguration.toString());
50              this.initialize();
51              this.chooseTypeAndRun(true);
52          }
53          catch (final Throwable throwable)
54          {
55              if (throwable instanceof AndroMDAppException)
56              {
57                  throw (AndroMDAppException)throwable;
58              }
59              throw new AndroMDAppException(throwable);
60          }
61      }
62  
63      /**
64       * The name of the AndroMDApp descriptor.
65       */
66      static final String DESCRIPTOR = "andromdapp.xml";
67  
68      /**
69       * The directory in which the descriptors are kept.
70       */
71      private static final String DESCRIPTOR_DIRECTORY = "META-INF/andromdapp";
72  
73      /**
74       * All types of discovered AndroMDApps
75       */
76      private final Map<String, AndroMDAppType> types = new LinkedHashMap<String, AndroMDAppType>();
77  
78      /**
79       * Performs any required initialization.
80       * @throws Exception
81       */
82      private void initialize()
83          throws Exception
84      {
85          final URL[] descriptorDirectories = ResourceFinder.findResources(DESCRIPTOR_DIRECTORY);
86          if (descriptorDirectories != null && descriptorDirectories.length > 0)
87          {
88              final int numberOfDescriptorDirectories = descriptorDirectories.length;
89              for (int ctr = 0; ctr < numberOfDescriptorDirectories; ctr++)
90              {
91                  final List<String> directoryContents =
92                      ResourceUtils.getDirectoryContents(
93                          descriptorDirectories[ctr],
94                          true,
95                          null);
96                  for (final String uri : directoryContents)
97                  {
98                      if (uri.replaceAll(".*(\\\\+|/)","").equals(DESCRIPTOR))
99                      {
100                         final XmlObjectFactory factory = XmlObjectFactory.getInstance(AndroMDApp.class);
101                         final URL descriptorUri = ResourceUtils.toURL(uri);
102                         final AndroMDAppType andromdapp = (AndroMDAppType) factory.getObject(descriptorUri);
103                         andromdapp.setResource(descriptorUri);
104                         final String type = andromdapp.getType();
105                         AndroMDALogger.info("discovered andromdapp type --> '" + type + '\'');
106                         this.types.put(
107                                 type,
108                                 andromdapp);
109                     }
110                 }
111             }
112         }
113     }
114 
115     /**
116      * Stores the optional configuration instance.
117      */
118     private final List<Configuration> configurations = new ArrayList<Configuration>();
119 
120     /**
121      * Adds the URI for an optional configuration  These are useful if you want
122      * to preconfigure the andromdapp when any properties, etc.
123      *
124      * @param configurationUri the URI to the configuration.
125      */
126     public void addConfigurationUri(final String configurationUri)
127     {
128         if (StringUtils.isNotBlank(configurationUri))
129         {
130             final XmlObjectFactory factory = XmlObjectFactory.getInstance(Configuration.class);
131             final URL configurationUrl = ResourceUtils.toURL(configurationUri);
132             if (configurationUrl == null)
133             {
134                 throw new AndroMDAppException("configuriationUri is invalid --> '" + configurationUri + '\'');
135             }
136             this.configurations.add((Configuration)factory.getObject(configurationUrl));
137         }
138     }
139 
140     /**
141      * Adds the configuration contents stored as a String.
142      *
143      * @param configuration the configuration contents as a string.
144      */
145     public void addConfiguration(final String configuration)
146     {
147         if (StringUtils.isNotBlank(configuration))
148         {
149             final XmlObjectFactory factory = XmlObjectFactory.getInstance(Configuration.class);
150             this.configurations.add((Configuration)factory.getObject(configuration));
151         }
152     }
153 
154     /**
155      * Prompts the user to choose the type of application, and then runs that AndroMDAppType.
156      * @throws Exception
157      */
158     private List<File> chooseTypeAndRun(final boolean write)
159         throws Exception
160     {
161         if (this.types.isEmpty())
162         {
163             throw new AndroMDAppException("No '" + DESCRIPTOR + "' descriptor files could be found");
164         }
165         final Map<String, String> properties = new LinkedHashMap<String, String>();
166         for (Configuration configuration : this.configurations)
167         {
168             properties.putAll(configuration.getAllProperties());
169         }
170         final String applicationType = properties.get(APPLICATION_TYPE);
171         final Set<String> validTypes = this.types.keySet();
172         AndroMDAppType andromdapp = this.types.get(applicationType);
173         if (andromdapp == null)
174         {
175             if (this.types.size() > 1)
176             {
177                 final StringBuilder typesChoice = new StringBuilder("[");
178                 for (final Iterator<String> iterator = validTypes.iterator(); iterator.hasNext();)
179                 {
180                     final String type = iterator.next();
181                     typesChoice.append(type);
182                     if (iterator.hasNext())
183                     {
184                         typesChoice.append(", ");
185                     }
186                 }
187                 typesChoice.append(']');
188                 this.printText("Please choose the type of application to generate " + typesChoice);
189                 String selectedType = this.readLine();
190                 while (!this.types.containsKey(selectedType))
191                 {
192                     selectedType = this.readLine();
193                 }
194                 andromdapp = this.types.get(selectedType);
195             }
196             else if (!this.types.isEmpty())
197             {
198                 andromdapp = this.types.entrySet().iterator().next().getValue();
199             }
200         }
201 
202         if (andromdapp == null)
203         {
204             throw new AndroMDAppException("AndromdaPP is null " + this.types.entrySet());
205         }
206         andromdapp.setConfigurations(this.configurations);
207         andromdapp.initialize();
208 
209         final Map templateContext = andromdapp.getTemplateContext();
210 
211         final XmlObjectFactory factory = XmlObjectFactory.getInstance(AndroMDApp.class);
212         final String contents = andromdapp.promptUser();
213         // - evaluate all properties in the descriptor and recreate the AndroMDAppType
214         andromdapp = (AndroMDAppType)factory.getObject(contents);
215         andromdapp.setConfigurations(this.configurations);
216         andromdapp.addToTemplateContext(templateContext);
217         return andromdapp.processResources(write);
218     }
219 
220     /**
221      * Identifies the AndroMDApp type (used to override the prompting of the type).
222      */
223     private static final String APPLICATION_TYPE = "andromdappType";
224 
225     /**
226      * Removes all structure generated from the previous run.
227      */
228     public void clean()
229     {
230         try
231         {
232             AndroMDALogger.initialize();
233             this.initialize();
234             final List<File> list = this.chooseTypeAndRun(false);
235             for (final File file : list)
236             {
237                 this.deleteFile(file);
238             }
239         }
240         catch (final Throwable throwable)
241         {
242             if (throwable instanceof AndroMDAppException)
243             {
244                 throw (AndroMDAppException)throwable;
245             }
246             throw new AndroMDAppException(throwable);
247         }
248     }
249 
250     /**
251      * Deletes the given file and any empty parent directories
252      * that the file might be contained within.
253      *
254      * @param file the file to remove.
255      * @throws MalformedURLException
256      */
257     private void deleteFile(final File file) throws MalformedURLException
258     {
259         if (file != null && file.exists())
260         {
261             final File[] children = file.listFiles();
262             if (children == null || children.length == 0)
263             {
264                 if (file.delete())
265                 {
266                     AndroMDALogger.info("Removed: '" + file.toURI().toURL() + '\'');
267                 }
268                 this.deleteFile(file.getParentFile());
269             }
270         }
271     }
272 
273     /**
274      * Prints text to the console.
275      *
276      * @param text the text to print to the console;
277      */
278     private void printText(final String text)
279     {
280         System.out.println();  // NOPMD - have to print to console prompt
281         System.out.println(text);  // NOPMD - have to print to console prompt
282         System.out.flush();
283     }
284 
285     /**
286      * Reads a line from standard input and returns the value.
287      *
288      * @return the value read from standard input.
289      */
290     private String readLine()
291     {
292         final BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
293         String inputString = null;
294         try
295         {
296             inputString = input.readLine();
297         }
298         catch (final IOException exception)
299         {
300             AndroMDALogger.info("AndroMDApp Error reading inputLine from System.in");
301         }
302         return StringUtils.trimToNull(inputString);
303     }
304 }