001: /*
002: * Project: AMODA - Abstract Modeled Application
003: * Class: de.gulden.framework.amoda.generic.core.GenericApplication
004: * Version: snapshot-beautyj-1.1
005: *
006: * Date: 2004-09-29
007: *
008: * This is a snapshot version of the AMODA 0.2 development branch,
009: * it is not released as a seperate version.
010: * For AMODA, see http://amoda.berlios.de/.
011: *
012: * This is licensed under the GNU Lesser General Public License (LGPL)
013: * and comes with NO WARRANTY.
014: *
015: * Author: Jens Gulden
016: * Email: amoda@jensgulden.de
017: */
018:
019: package de.gulden.framework.amoda.generic.core;
020:
021: import de.gulden.framework.amoda.generic.core.GenericApplicationEnvironmentFactory;
022: import de.gulden.framework.amoda.generic.metadata.*;
023: import de.gulden.framework.amoda.generic.option.*;
024: import de.gulden.framework.amoda.model.behaviour.Command;
025: import de.gulden.framework.amoda.model.behaviour.event.*;
026: import de.gulden.framework.amoda.model.core.*;
027: import de.gulden.framework.amoda.model.core.Application;
028: import de.gulden.framework.amoda.model.data.*;
029: import de.gulden.framework.amoda.model.document.*;
030: import de.gulden.framework.amoda.model.interaction.*;
031: import de.gulden.framework.amoda.model.option.*;
032: import de.gulden.framework.amoda.model.option.Option;
033: import de.gulden.util.*;
034: import java.lang.*;
035: import java.net.*;
036: import java.util.*;
037: import javax.swing.*;
038: import org.w3c.dom.*;
039: import org.w3c.dom.Document;
040:
041: /**
042: * Class GenericApplication.
043: *
044: * @author Jens Gulden
045: * @version snapshot-beautyj-1.1
046: */
047: public abstract class GenericApplication extends
048: GenericApplicationModule implements Application, Command,
049: LogPerformer {
050:
051: // ------------------------------------------------------------------------
052: // --- fields ---
053: // ------------------------------------------------------------------------
054:
055: public static String DEFAULT_CONFIGURATION_RESOURCE = System
056: .getProperty("application.config", "res:application.xml");
057:
058: public static final int YES = 0;
059:
060: public static final int NO = 2;
061:
062: public static final int CANCEL = 4;
063:
064: public boolean verbose;
065:
066: public boolean quiet;
067:
068: protected Option[] simpleAnswerOptions;
069:
070: protected Queue commandQueue;
071:
072: protected Stack commandUndoStack;
073:
074: protected Document configuration;
075:
076: protected DocumentHandler documentHandler;
077:
078: protected RecentFilesList recentFilesList;
079:
080: public static final String OPTIONS_DIALOG_TITLE = "Options";
081:
082: protected GenericApplicationEnvironment genericApplicationEnvironment;
083:
084: public UndoStack undoStack;
085:
086: public Collection applicationListener = new ArrayList();
087:
088: // ------------------------------------------------------------------------
089: // --- constructor ---
090: // ------------------------------------------------------------------------
091:
092: public GenericApplication() {
093: super ();
094: setDocumentHandler(createDocumentHandler()); // default impl.
095: setRecentFilesList(createRecentFilesList());
096: }
097:
098: // ------------------------------------------------------------------------
099: // --- methods ---
100: // ------------------------------------------------------------------------
101:
102: public GenericApplicationEnvironment getGenericApplicationEnvironment() {
103: return genericApplicationEnvironment;
104: }
105:
106: public void setGenericApplicationEnvironment(
107: GenericApplicationEnvironment genericApplicationEnvironment) {
108: if (this .genericApplicationEnvironment != genericApplicationEnvironment) {
109: this .genericApplicationEnvironment = genericApplicationEnvironment;
110: if (genericApplicationEnvironment != null)
111: genericApplicationEnvironment
112: .setGenericApplication(this );
113: }
114: }
115:
116: public UndoStack getUndoStack() {
117: return undoStack;
118: }
119:
120: public void setUndoStack(UndoStack undoStack) {
121: this .undoStack = undoStack;
122: }
123:
124: public Collection getApplicationListeners() {
125: return applicationListener;
126: }
127:
128: public void addApplicationListener(
129: ApplicationListener applicationListener) {
130: if (!this .applicationListener.contains(applicationListener))
131: this .applicationListener.add(applicationListener);
132: }
133:
134: public void removeApplicationListener(
135: ApplicationListener applicationListener) {
136: this .applicationListener.remove(applicationListener);
137: }
138:
139: public void log(String text, Object source) {
140: de.gulden.framework.amoda.model.interaction.LogMessage m = getGenericApplicationEnvironment()
141: .createDefaultLogMessage(text, source,
142: Message.INFORMATION_MESSAGE, null);
143: m.perform();
144: }
145:
146: public void init(ApplicationEnvironment environment) {
147: GenericApplicationEnvironment env = (GenericApplicationEnvironment) environment; // throws ClastCastException if invalid environment
148: setGenericApplicationEnvironment(env);
149: simpleAnswerOptions = new de.gulden.framework.amoda.model.option.Option[3];
150: simpleAnswerOptions[0] = env.createDefaultOption("YES",
151: Boolean.FALSE, env.createDefaultMetadata("Yes"));
152: simpleAnswerOptions[1] = env.createDefaultOption("NO",
153: Boolean.FALSE, env.createDefaultMetadata("No"));
154: simpleAnswerOptions[2] = env.createDefaultOption("CANCEL",
155: Boolean.FALSE, env.createDefaultMetadata("Cancel"));
156: undoStack = new UndoStack();
157: this .initModule(this );
158: }
159:
160: public void start() {
161: super .start();
162: // to be extended or overwritten by subclass
163: if (getOptions().getBoolean(
164: GenericApplicationEnvironment.CONFIGURATION_BATCH_MODE)) {
165: Collection batchCommands = ((GenericApplicationEnvironment) getEnvironment())
166: .getBatchCommands();
167: // do batch commands before starting interaction
168: for (Iterator it = batchCommands.iterator(); it.hasNext();) {
169: de.gulden.framework.amoda.model.behaviour.Command c = (de.gulden.framework.amoda.model.behaviour.Command) it
170: .next();
171: c.perform();
172: }
173: }
174: if (getOptions()
175: .getBoolean(
176: GenericApplicationEnvironment.CONFIGURATION_INTERACTION_MODE)) {
177: startInteraction();
178: }
179: }
180:
181: public void message(String title, String text) {
182: de.gulden.framework.amoda.model.interaction.Message m = getGenericApplicationEnvironment()
183: .createDefaultMessage(title, text,
184: Message.INFORMATION_MESSAGE, null);
185: m.perform();
186: }
187:
188: public void message(String text) {
189: message("Message", text);
190: }
191:
192: public void log(String text) {
193: log(text, this );
194: }
195:
196: public void error(String text, Throwable throwable,
197: boolean exitApplication) {
198: de.gulden.framework.amoda.model.interaction.ErrorMessage m = getGenericApplicationEnvironment()
199: .createDefaultErrorMessage(text, throwable,
200: exitApplication, null);
201: m.perform();
202: }
203:
204: public String question(String title, String text, String answers) {
205: de.gulden.framework.amoda.model.interaction.Question q = getGenericApplicationEnvironment()
206: .createDefaultQuestion(title, text, answers, null);
207: getGenericApplicationEnvironment().doQuestion(q);
208: return q.getAnswer();
209: }
210:
211: public String question(String text, String answers) {
212: return question("", text, answers);
213: }
214:
215: public boolean confirm(String title, String text) {
216: return question(title, text, "yes,no").equals("yes");
217: }
218:
219: public boolean confirm(String text) {
220: return confirm("", text);
221: }
222:
223: public void run() {
224: ApplicationEnvironment env = createApplicationEnvironment();
225: env.launch(this );
226: }
227:
228: public void startInteraction() {
229: // your code here
230: }
231:
232: public void about() {
233: ((GenericApplicationEnvironment) getEnvironment()).doAbout();
234: }
235:
236: public void welcome() {
237: // your code here
238: }
239:
240: public void usage(boolean exitApplication) {
241: ((GenericApplicationEnvironment) getEnvironment())
242: .doUsage(exitApplication);
243: }
244:
245: public void usage(String errorText) {
246: message("Error: " + errorText);
247: usage(true);
248: }
249:
250: public void error(Throwable throwable, boolean exit) {
251: error("", throwable, exit);
252: }
253:
254: public Application getApplication() {
255: return this ;
256: }
257:
258: public ApplicationEnvironment getEnvironment() {
259: return getGenericApplicationEnvironment();
260: }
261:
262: public void error(String text, Throwable cause) {
263: error(text, cause, isBatchMode());
264: }
265:
266: public void error(String text) {
267: error(text, null);
268: }
269:
270: public void fatalError(String text, Throwable cause) {
271: error(text, cause, true);
272: }
273:
274: public void fatalError(String text) {
275: fatalError(text, null);
276: }
277:
278: public void error(Throwable cause) {
279: error("", cause);
280: }
281:
282: public void fatalError(Throwable cause) {
283: String msg = cause.getMessage();
284: if (msg == null) {
285: msg = "-" + cause.getClass().getName() + "-";
286: }
287: fatalError(msg, cause);
288: }
289:
290: public URL getResource(String res) {
291: URL url;
292: if (res.startsWith("/")) { // absolute URL
293: url = this .getClass().getResource(res);
294: } else {
295: String resBase = getOptions().getString("resource-base");
296: if (!resBase.endsWith("/"))
297: resBase += "/";
298: url = this .getClass().getResource(resBase + res);
299: if (url == null) { // not yet found, try in system-resources
300: resBase = getOptions().getString(
301: "environment-resource-base");
302: if (!resBase.endsWith("/"))
303: resBase += "/";
304: url = this .getClass().getResource(resBase + res);
305: // is still not found, url==null
306: }
307: }
308: return url;
309: }
310:
311: public ImageIcon getImage(String res) {
312: return new ImageIcon(getResource(res));
313: }
314:
315: public Workspace getWorkspace() {
316: return getGenericApplicationEnvironment().getWorkspace();
317: }
318:
319: public ArgsParser createArgsParser() {
320: // your code here
321: return null;
322: }
323:
324: public Workspace createWorkspace() {
325: return getGenericApplicationEnvironment()
326: .createDefaultWorkspace();
327: }
328:
329: public void status(String text) {
330: Message m = getGenericApplicationEnvironment()
331: .createDefaultMessage(null, text,
332: Message.STATUS_MESSAGE, null);
333: m.perform();
334: }
335:
336: public DocumentHandler getDocumentHandler() {
337: return documentHandler;
338: }
339:
340: public void setDocumentHandler(DocumentHandler _documentHandler) {
341: documentHandler = _documentHandler;
342: }
343:
344: public RecentFilesList getRecentFilesList() {
345: return recentFilesList;
346: }
347:
348: public void setRecentFilesList(RecentFilesList _recentFilesList) {
349: recentFilesList = _recentFilesList;
350: }
351:
352: public boolean isVerbose() {
353: try {
354: return verbose || getOptions().getBoolean("verbose");
355: } catch (IllegalOptionError ioe) {
356: return verbose;
357: }
358: }
359:
360: public void setVerbose(boolean _verbose) {
361: verbose = _verbose;
362: }
363:
364: public boolean isQuiet() {
365: return quiet || getOptions().getBoolean("quiet");
366: }
367:
368: public void setQuiet(boolean _quiet) {
369: quiet = _quiet;
370: }
371:
372: public void exit() {
373: exit(0);
374: }
375:
376: public void exit(int code) {
377: getGenericApplicationEnvironment().doExit(this , code);
378: }
379:
380: public boolean isBatchMode() {
381: return getOptions().getBoolean("batch-mode");
382: }
383:
384: public Value[] getInputValues() {
385: return getGenericApplicationEnvironment().getInputValues();
386: }
387:
388: public void optionsDialog() {
389: de.gulden.framework.amoda.generic.interaction.GenericDialog d = new de.gulden.framework.amoda.generic.interaction.GenericDialog();
390: d.setTitle(OPTIONS_DIALOG_TITLE);
391: d.setParent(this );
392: d.setOptionsDirectly(getOptions()); // ..._Directly_ to avoid setting back-reference as parent member of options
393: d.perform();
394: if (d.getAnswer() == d.OK) {
395: ((de.gulden.framework.amoda.generic.option.GenericOptions) getOptions())
396: .saveGlobalProperties();
397: }
398: }
399:
400: public void perform() {
401: // your code here
402: }
403:
404: public void help() {
405: ((GenericApplicationEnvironment) getEnvironment()).doHelp();
406: }
407:
408: public Collection getAvailableFeatures() {
409: ArrayList features = new ArrayList();
410: for (Iterator it = getFeatures().iterator(); it.hasNext();) {
411: GenericFeature f = (GenericFeature) it.next();
412: if (!f.isSystem()) {
413: features.add(f);
414: }
415: }
416: return features;
417: }
418:
419: public synchronized boolean isInInitializing() {
420: return true;
421: }
422:
423: public synchronized boolean isInRunning() {
424: return true;
425: }
426:
427: public synchronized boolean isInWaitingForCommand() {
428: return true;
429: }
430:
431: public synchronized boolean isInExecutingCommand() {
432: return true;
433: }
434:
435: public synchronized boolean isInDestroying() {
436: return true;
437: }
438:
439: protected abstract ApplicationEnvironment createApplicationEnvironment();
440:
441: protected Message createAboutMessage() {
442: String name = getMetadata().get("title");
443: if (Toolbox.empty(name)) {
444: name = getMetadata().get("name");
445: if (Toolbox.empty(name)) {
446: name = "";
447: }
448: }
449: String version = getMetadata().get("version");
450: if (!Toolbox.empty(version)) {
451: version = " " + version;
452: } else {
453: version = "";
454: }
455: String author = getMetadata().get("author");
456: if (!Toolbox.empty(author)) {
457: author = " by " + author;
458: String email = getMetadata().get("email");
459: if (!Toolbox.empty(email)) {
460: email = ", " + email;
461: author = author + email;
462: }
463: } else {
464: author = "";
465: }
466: String license = getMetadata().get("license-message"); // metadata description by default
467: if (!Toolbox.empty(license)) {
468: license = NL + license;
469: } else {
470: license = "";
471: }
472: Message m = getGenericApplicationEnvironment()
473: .createDefaultMessage("about",
474: name + version + author + license, 0, null);
475: return m;
476: }
477:
478: protected Message createUsageMessage() {
479: StringBuffer sb = new StringBuffer();
480: String description = getMetadata().get("description").trim();
481: if (description.length() != 0) {
482: sb.append(description + NL + NL);
483: }
484: String usageLine = getUsageLine();
485: if (usageLine != null) {
486: sb.append("Usage: " + usageLine + NL);
487: }
488: return getGenericApplicationEnvironment().createDefaultMessage(
489: "Usage", sb.toString(), 0, null);
490: }
491:
492: protected DocumentHandler createDocumentHandler() {
493: // your code here
494: return null;
495: }
496:
497: protected ClipboardHandler createClipboardHandler() {
498: // your code here
499: return null;
500: }
501:
502: protected RecentFilesList createRecentFilesList() {
503: return new GenericRecentFilesList();
504: }
505:
506: protected URL getDefaultConfigurationDocumentResource() {
507: URL res;
508: try {
509: res = Toolbox
510: .getResourceURL(DEFAULT_CONFIGURATION_RESOURCE);
511: } catch (java.net.MalformedURLException mue) {
512: res = null;
513: }
514: if (res != null) {
515: return res;
516: } else {
517: return super .getDefaultConfigurationDocumentResource();
518: }
519: }
520:
521: protected Message createHelpMessage() {
522: StringBuffer sb = new StringBuffer();
523: Collection allOptions = getOptions()
524: .getAll(
525: de.gulden.framework.amoda.model.option.OptionEntry.class,
526: true).values();
527: if (!allOptions.isEmpty()) {
528: sb.append("options are: " + NL);
529: double[] weights = { 0.0, 0.0, 1.0 };
530: de.gulden.util.text.TextTable table = new de.gulden.util.text.TextTable(
531: 3, 79, weights, ' ');
532: for (Iterator it = allOptions.iterator(); it.hasNext();) {
533: de.gulden.framework.amoda.generic.option.GenericOptionEntry o = (de.gulden.framework.amoda.generic.option.GenericOptionEntry) it
534: .next();
535: if (!(o.isSystem())) {
536: String name = "-" + o.getId();
537: String shortcut = o.getShortcut();
538: if (shortcut != null) {
539: name = "-" + shortcut + " or\n" + name;
540: }
541: String description = de.gulden.util.Toolbox
542: .noNull(o.getMetadata().get("description"));
543: String type = "<"
544: + de.gulden.util.Toolbox.unqualify(
545: o.getType().getName())
546: .toLowerCase() + ">";
547: String defaultValue = o.getValue(o.STATE_DEFAULT)
548: .getString();
549: if (defaultValue != null) {
550: description += " (default: " + defaultValue
551: + ")";
552: }
553: String[] row = { name, type, description };
554: table.addRow(row);
555: }
556: }
557: sb.append(table.toString());
558: }
559: Message usage = createUsageMessage();
560: return getGenericApplicationEnvironment().createDefaultMessage(
561: "Help", usage.getText() + NL + sb.toString(), 0, null);
562: }
563:
564: protected String getUsageLine() {
565: String u = getMetadata().get("usage").trim();
566: if (u.length() == 0) {
567: u = getDefaulUsageLine();
568: }
569: return u;
570: }
571:
572: protected String getDefaulUsageLine() {
573: if (getOptions()
574: .getBoolean(
575: GenericApplicationEnvironment.CONFIGURATION_PERSISTENT_OPTIONS)) {
576: return "[java-and-classpath] " + findTitle().toLowerCase()
577: + " -properties [properties-file]";
578: } else {
579: ArrayList relevantFeatures = new ArrayList();
580: for (Iterator it = getAvailableFeatures().iterator(); it
581: .hasNext();) {
582: Feature f = (Feature) it.next();
583: if ((!(f.getId().equals("default") || f.getId().equals(
584: "exit")))
585: && (!((f instanceof de.gulden.framework.amoda.model.core.FeatureGroup) || (f instanceof de.gulden.framework.amoda.model.interaction.InteractionMember)))) {
586: relevantFeatures.add(f);
587: }
588: }
589: return "[java-and-classpath] "
590: + findTitle().toLowerCase()
591: + " "
592: + ((relevantFeatures.size() > 0) ? "[commands] "
593: : "") + "[options] [input]";
594: }
595: }
596:
597: void initModule(GenericApplicationModule module) {
598: // load configuration from XML
599: org.w3c.dom.Element tag = module.getConfigurationDocument()
600: .getDocumentElement();
601: try {
602: de.gulden.util.xml.serializer.XMLSerializerFactory factory = getGenericApplicationEnvironment()
603: .getFactory().getXMLSerializerFactory();
604: de.gulden.util.xml.serializer.XMLSerializer ser = factory
605: .createXMLSerializer();
606: ser.xmlDeserialize(module, tag);
607: if (this != module) { // also deserialize on this to get new options and features
608: de.gulden.framework.amoda.model.metadata.Metadata rememberMetadata = this
609: .getMetadata();
610: this .setMetadata(null); // DIRTY!
611: ser.xmlDeserialize(this , tag); // !!! to get options etc. of module into application, EXPERIMENTAL
612: this .setMetadata(rememberMetadata); // DIRTY!
613: }
614: } catch (de.gulden.util.xml.XMLException e) {
615: fatalError("can't parse configuration XML", e);
616: }
617: // re-init options
618: getGenericApplicationEnvironment().initApplicationOptions(this );
619: // init modules itself
620: module.init();
621: // inform listeners
622: Toolbox
623: .fireEvent(
624: getApplicationListeners(),
625: "moduleLoaded",
626: new de.gulden.framework.amoda.model.behaviour.event.ApplicationEvent(
627: module));
628: }
629:
630: } // end GenericApplication
|