001: package org.andromda.andromdapp;
002:
003: import java.io.BufferedReader;
004: import java.io.File;
005: import java.io.IOException;
006: import java.io.InputStreamReader;
007: import java.net.MalformedURLException;
008: import java.net.URL;
009: import java.util.ArrayList;
010: import java.util.Iterator;
011: import java.util.LinkedHashMap;
012: import java.util.List;
013: import java.util.Map;
014: import java.util.Set;
015:
016: import org.andromda.core.common.AndroMDALogger;
017: import org.andromda.core.common.ResourceFinder;
018: import org.andromda.core.common.ResourceUtils;
019: import org.andromda.core.common.XmlObjectFactory;
020:
021: /**
022: * Represents an instance of the AndroMDA application
023: * generator.
024: *
025: * @author Chad Brandon
026: */
027: public class AndroMDApp {
028:
029: /**
030: * An AndroMDApp configuration that contains some internal configuration information (like the AndroMDA
031: * version, and other common properties).
032: */
033: private static final String INTERNAL_CONFIGURATION_URI = "META-INF/andromdapp/configuration.xml";
034:
035: /**
036: * Runs the AndroMDApp generation process.
037: */
038: public void run() {
039: try {
040: AndroMDALogger.initialize();
041: final URL internalConfiguration = ResourceUtils
042: .getResource(INTERNAL_CONFIGURATION_URI);
043: if (internalConfiguration == null) {
044: throw new AndroMDAppException(
045: "No configuration could be loaded from --> '"
046: + INTERNAL_CONFIGURATION_URI + "'");
047: }
048: this .addConfigurationUri(internalConfiguration.toString());
049: this .initialize();
050: this .chooseTypeAndRun(true);
051: } catch (final Throwable throwable) {
052: if (throwable instanceof AndroMDAppException) {
053: throw (AndroMDAppException) throwable;
054: }
055: throw new AndroMDAppException(throwable);
056: }
057: }
058:
059: /**
060: * The name of the AndroMDApp descriptor.
061: */
062: static final String DESCRIPTOR = "andromdapp.xml";
063:
064: /**
065: * The directory in which the descriptors are kept.
066: */
067: private static final String DESCRIPTOR_DIRECTORY = "META-INF/andromdapp";
068:
069: /**
070: * All types of discovered AndroMDApps
071: */
072: private Map types = new LinkedHashMap();
073:
074: /**
075: * Performs any required initialization.
076: * @throws Exception
077: */
078: private void initialize() throws Exception {
079: final URL[] descriptorDirectories = ResourceFinder
080: .findResources(DESCRIPTOR_DIRECTORY);
081: if (descriptorDirectories != null
082: && descriptorDirectories.length > 0) {
083: final int numberOfDescriptorDirectories = descriptorDirectories.length;
084: for (int ctr = 0; ctr < numberOfDescriptorDirectories; ctr++) {
085: final List directoryContents = ResourceUtils
086: .getDirectoryContents(
087: descriptorDirectories[ctr], true, null);
088: for (final Iterator iterator = directoryContents
089: .iterator(); iterator.hasNext();) {
090: final String uri = (String) iterator.next();
091: if (uri.replaceAll(".*(\\\\+|/)", "").equals(
092: DESCRIPTOR)) {
093: final XmlObjectFactory factory = XmlObjectFactory
094: .getInstance(AndroMDApp.class);
095: final URL descriptorUri = ResourceUtils
096: .toURL(uri);
097: final AndroMDAppType andromdapp = (AndroMDAppType) factory
098: .getObject(descriptorUri);
099: andromdapp.setResource(descriptorUri);
100: final String type = andromdapp.getType();
101: AndroMDALogger
102: .info("discovered andromdapp type --> '"
103: + type + "'");
104: this .types.put(type, andromdapp);
105: }
106: }
107: }
108: }
109: }
110:
111: /**
112: * Stores the optional configuration instance.
113: */
114: private List configurations = new ArrayList();
115:
116: /**
117: * Adds the URI for an optional configuration These are useful if you want
118: * to preconfigure the andromdapp when any properties, etc.
119: *
120: * @param configurationUri the URI to the configuration.
121: */
122: public void addConfigurationUri(final String configurationUri) {
123: if (configurationUri != null
124: && configurationUri.trim().length() > 0) {
125: final XmlObjectFactory factory = XmlObjectFactory
126: .getInstance(Configuration.class);
127: final URL configurationUrl = ResourceUtils
128: .toURL(configurationUri);
129: if (configurationUrl == null) {
130: throw new AndroMDAppException(
131: "configuriationUri is invalid --> '"
132: + configurationUri + "'");
133: }
134: this .configurations.add(factory.getObject(ResourceUtils
135: .toURL(configurationUri)));
136: }
137: }
138:
139: /**
140: * Adds the configuration contents stored as a String.
141: *
142: * @param configuration the configuration contents as a string.
143: */
144: public void addConfiguration(final String configuration) {
145: if (configuration != null && configuration.trim().length() > 0) {
146: final XmlObjectFactory factory = XmlObjectFactory
147: .getInstance(Configuration.class);
148: this .configurations.add(factory.getObject(configuration));
149: }
150: }
151:
152: /**
153: * Prompts the user to choose the type of application, and then runs that AndroMDAppType.
154: * @throws Exception
155: */
156: private List chooseTypeAndRun(boolean write) throws Exception {
157: if (this .types.isEmpty()) {
158: throw new AndroMDAppException("No '" + DESCRIPTOR
159: + "' descriptor files could be found");
160: }
161: final Map properties = new LinkedHashMap();
162: for (final Iterator iterator = this .configurations.iterator(); iterator
163: .hasNext();) {
164: Configuration configuration = (Configuration) iterator
165: .next();
166: properties.putAll(configuration.getAllProperties());
167: }
168: final String applicationType = (String) properties
169: .get(APPLICATION_TYPE);
170: final Set validTypes = this .types.keySet();
171: AndroMDAppType andromdapp = (AndroMDAppType) this .types
172: .get(applicationType);
173: if (andromdapp == null) {
174: if (this .types.size() > 1) {
175: final StringBuffer typesChoice = new StringBuffer("[");
176: for (final Iterator iterator = validTypes.iterator(); iterator
177: .hasNext();) {
178: final String type = (String) iterator.next();
179: typesChoice.append(type);
180: if (iterator.hasNext()) {
181: typesChoice.append(", ");
182: }
183: }
184: typesChoice.append("]");
185: this
186: .printText("Please choose the type of application to generate "
187: + typesChoice);
188: String selectedType = this .readLine();
189: while (!this .types.containsKey(selectedType)) {
190: selectedType = this .readLine();
191: }
192: andromdapp = (AndroMDAppType) this .types
193: .get(selectedType);
194: } else if (!this .types.isEmpty()) {
195: andromdapp = (AndroMDAppType) ((Map.Entry) this .types
196: .entrySet().iterator().next()).getValue();
197: }
198: }
199:
200: andromdapp.setConfigurations(this .configurations);
201: andromdapp.initialize();
202:
203: final Map templateContext = andromdapp.getTemplateContext();
204:
205: final XmlObjectFactory factory = XmlObjectFactory
206: .getInstance(AndroMDApp.class);
207: final String contents = andromdapp.promptUser();
208: // - evaluate all properties in the descriptor and recreate the AndroMDAppType
209: andromdapp = (AndroMDAppType) factory.getObject(contents);
210: andromdapp.setConfigurations(this .configurations);
211: andromdapp.addToTemplateContext(templateContext);
212: return andromdapp.processResources(write);
213: }
214:
215: /**
216: * Identifies the AndroMDApp type (used to override the prompting of the type).
217: */
218: private static final String APPLICATION_TYPE = "andromdappType";
219:
220: /**
221: * Removes all structure generated from the previous run.
222: */
223: public void clean() {
224: try {
225: AndroMDALogger.initialize();
226: this .initialize();
227: final List list = this .chooseTypeAndRun(false);
228: for (final Iterator iterator = list.iterator(); iterator
229: .hasNext();) {
230: final File file = (File) iterator.next();
231: this .deleteFile(file);
232: }
233: } catch (final Throwable throwable) {
234: if (throwable instanceof AndroMDAppException) {
235: throw (AndroMDAppException) throwable;
236: }
237: throw new AndroMDAppException(throwable);
238: }
239: }
240:
241: /**
242: * Deletes the given file and any empty parent directories
243: * that the file might be contained within.
244: *
245: * @param file the file to remove.
246: * @throws MalformedURLException
247: */
248: private void deleteFile(final File file)
249: throws MalformedURLException {
250: if (file != null && file.exists()) {
251: final File[] children = file.listFiles();
252: if (children == null || children.length == 0) {
253: if (file.delete()) {
254: AndroMDALogger.info("Removed: '" + file.toURL()
255: + "'");
256: }
257: this .deleteFile(file.getParentFile());
258: }
259: }
260: }
261:
262: /**
263: * Prints text to the console.
264: *
265: * @param text the text to print to the console;
266: */
267: private void printText(final String text) {
268: System.out.println();
269: System.out.println(text);
270: System.out.flush();
271: }
272:
273: /**
274: * Reads a line from standard input and returns the value.
275: *
276: * @return the value read from standard input.
277: */
278: private String readLine() {
279: final BufferedReader input = new BufferedReader(
280: new InputStreamReader(System.in));
281: String inputString = null;
282: try {
283: inputString = input.readLine();
284: } catch (final IOException exception) {
285: inputString = null;
286: }
287: return inputString == null || inputString.trim().length() == 0 ? null
288: : inputString;
289: }
290: }
|