001: /*
002: * Copyright (C) 2007 Jared Alexander Spigner
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2.1 of the License, or any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: *
018: * jspigner@openjx.org
019: *
020: * VirtualMachine.java
021: *
022: * Created on June 8, 2007, 1:15 AM
023: *
024: */
025:
026: package org.openjx.core;
027:
028: import org.xml.sax.Attributes;
029:
030: import java.io.StringWriter;
031: import java.io.IOException;
032:
033: import java.util.Iterator;
034: import java.util.Vector;
035:
036: import javax.swing.JComponent;
037: import javax.swing.JOptionPane;
038:
039: import org.openjx.OpenJX;
040:
041: import org.openjx.conf.JXGLOBAL;
042: import org.openjx.conf.Plugin;
043: import org.openjx.conf.JXPluginLoader;
044:
045: import org.openjx.display.JXMessageBox;
046: import org.openjx.display.JXViewer;
047:
048: import org.openjx.parser.PluginParser;
049: import org.openjx.parser.XMLParser;
050:
051: /**
052: * This is the core class to the OpenJX application. It controls all
053: * activities, including interpretation, translation, compilation and
054: * serialization.
055: *
056: * @author Jared Spigner
057: */
058: public class VirtualMachine {
059: /** This is a reference to the viewer object. */
060: private JXViewer jxViewer;
061:
062: /** This is a reference to the main application file. */
063: private String applicationFile;
064:
065: /** This is a reference to the XML Parser. */
066: private XMLParser xmlParser;
067:
068: /** This is a reference to the JXCompiler. */
069: private JXCompiler jxCompiler;
070:
071: /** This is a reference to the JXInterpreter. */
072: private JXInterpreter jxInterpreter;
073:
074: /** This is a reference to the Plugin Parser. */
075: private PluginParser pluginParser;
076:
077: /** This string contains the last serialized form. */
078: private String serializedForm;
079:
080: /** This is a reference to the Plugin Loader. */
081: public JXPluginLoader jxPluginLoader;
082:
083: /**
084: * This is the top most level object. It is composes all objects that
085: * make up a single form hierarchy.
086: */
087: public JXObject stackObject;
088:
089: /**
090: * This is the constructor for the VirtualMachine class. It creates a new
091: * instance of VirtualMachine.
092: */
093: public VirtualMachine() {
094: this .stackObject = null;
095:
096: this .jxViewer = null;
097: this .xmlParser = new XMLParser(this );
098: this .jxCompiler = new JXCompiler(this );
099: this .jxInterpreter = new JXInterpreter(this );
100: this .pluginParser = new PluginParser(this );
101: this .jxPluginLoader = new JXPluginLoader();
102:
103: this .serializedForm = "";
104: }
105:
106: /**
107: * This method compiles the script into Java code which may be displayed or
108: * executed.
109: *
110: * @return true on success, else false on failure.
111: */
112: public boolean compileProgram() {
113: if (JXGLOBAL.DEBUG_COMPILE)
114: System.out.println("##### COMPILE STARTED #####");
115:
116: this .jxCompiler.setupScriptEngine();
117: this .jxCompiler.compileScript(this .stackObject);
118:
119: if (JXGLOBAL.DEBUG_COMPILE)
120: System.out.println("##### COMPILE COMPLETED #####");
121:
122: return true;
123: }
124:
125: /**
126: * This method displays the program.
127: *
128: * @param openJX points to an instance of the viewer.
129: * @param pageid is the id of the page we want to display (page ids start
130: * at 1 not 0).
131: *
132: * @return true on success, else false on failure.
133: */
134: public boolean displayProgram(OpenJX openJX, int pageid) {
135: openJX.getContentPane().removeAll();
136:
137: openJX.getContentPane().add(
138: (JComponent) this .getPage(pageid).getObjectLink());
139: openJX.setSize(((JComponent) this .getPage(pageid)
140: .getObjectLink()).getSize());
141: openJX.repaint();
142:
143: if (openJX.applicationMode.equals("desktop")) {
144: // Without this code, window wont initiallly display right.
145: openJX.jxWindow.setPreferredSize(openJX.getSize());
146: openJX.jxWindow.pack();
147: openJX.jxWindow.setTitle(this .stackObject.getName());
148: openJX.jxWindow.setVisible(true);
149: }
150:
151: return true;
152: }
153:
154: /**
155: * This method returns an instance of the applicationFile.
156: *
157: * @return an instance of the applicationFile.
158: */
159: public String getApplicationFile() {
160: return this .applicationFile;
161: }
162:
163: /**
164: * This method returns the bound property value.
165: *
166: * @param property is the binding.
167: *
168: * @return the value fo the binding.
169: */
170: public Object getBound(String property) {
171: return this .getJXCompiler().scriptEngine.get(property
172: .substring(1, property.length() - 1));
173: }
174:
175: /**
176: * This method returns an instance to the JXCompiler.
177: *
178: * @return an instance to the JXCompiler.
179: */
180: public JXCompiler getJXCompiler() {
181: return this .jxCompiler;
182: }
183:
184: /**
185: * This method returns an instance to the JXInterpreter.
186: *
187: * @return an instance to the JXInterpreter.
188: */
189: public JXInterpreter getJXInterpreter() {
190: return this .jxInterpreter;
191: }
192:
193: /**
194: * This method returns a page by index number.
195: *
196: * @param id is the index number we are searching for.
197: *
198: * @return the page associated with the given index or null on failure.
199: */
200: public JXObject getPage(int id) {
201: int count = 1;
202:
203: for (int i = 0; i < this .stackObject.getLength(); i++) {
204: JXObject tmpObject = this .stackObject.stack.elementAt(i);
205:
206: if (tmpObject.getTAG().equals("jxpage") && count == id)
207: return tmpObject;
208:
209: if (tmpObject.getTAG().equals("jxpage"))
210: count++;
211: }
212:
213: return null;
214: }
215:
216: /**
217: * This method returns an instance of the PluginParser.
218: *
219: * @return an instance of the plugin parser.
220: */
221: public PluginParser getPluginParser() {
222: return this .pluginParser;
223: }
224:
225: /**
226: * This method returns a reference to the last serialized form.
227: *
228: * @return a string reference to the serialized form.
229: */
230: public String getSerializedForm() {
231: return this .serializedForm;
232: }
233:
234: /**
235: * This method returns an instance of the JXViewer.
236: *
237: * @return an instance of the JXViewer or null.
238: */
239: public JXViewer getViewer() {
240: return this .jxViewer;
241: }
242:
243: /**
244: * This method compiles the script into Java code which may be displayed or
245: * executed.
246: *
247: * @return true on success, else false on failure.
248: */
249: public boolean interpretProgram() {
250: if (JXGLOBAL.DEBUG_COMPILE)
251: System.out.println("##### INTERPRET STARTED #####");
252:
253: this .jxInterpreter.setScriptEngine(
254: this .jxCompiler.scriptEngine,
255: this .jxCompiler.scriptEngineManager);
256: this .jxInterpreter.interpretScript(this .stackObject);
257:
258: if (JXGLOBAL.DEBUG_COMPILE)
259: System.out.println("##### INTERPRET COMPLETED #####");
260:
261: return true;
262: }
263:
264: /**
265: * This method returns true if a property is bound syntax wise else false.
266: *
267: * @param property is the property we want to check.
268: *
269: * @return true on binding syntax, else false if not bound.
270: */
271: public boolean isBound(String property) {
272: return property.startsWith("@") && property.endsWith("@");
273: }
274:
275: /**
276: * This method loads the plugin list into the classpath and hence makes
277: * them accessible. Currently this does not return false if a plugin fails
278: * . We may want to change this in the future.
279: *
280: * @return true on success, else false on failure.
281: */
282: public boolean loadPlugins() {
283: Vector<Plugin> list = this .pluginParser.getPluginList();
284:
285: for (int i = 0; i < list.size(); i++) {
286: Plugin plugin = list.elementAt(i);
287: plugin.installPlugin(this );
288: if (!plugin.configurePlugin(this )) {
289: return false;
290: }
291: }
292:
293: return true;
294: }
295:
296: /**
297: * This method parses the plugin list from the .jx application file.
298: *
299: * @return true on success, else false on failure.
300: */
301: public boolean parsePlugins() {
302: if (!this .pluginParser.parsePlugins()) {
303: return false;
304: }
305:
306: return true;
307: }
308:
309: /**
310: * This method initializes the XML parser and begins the validating and
311: * parsing routines of the .jx application file.
312: *
313: * @return true on success, else false on failure.
314: */
315: public boolean parseProgram() {
316: if (JXGLOBAL.DEBUG_PARSER)
317: System.out.println("##### PARSER STARTED #####");
318:
319: /** Parse the form XML file into a FJO. */
320: if (!this .xmlParser.parseXML(this .applicationFile)) {
321: if (JXGLOBAL.DEBUG_PARSER)
322: System.out
323: .println("##### PARSER COMPLETED WITH ERROR #####");
324: return false;
325: }
326:
327: if (JXGLOBAL.DEBUG_PARSER)
328: System.out.println("##### PARSER COMPLETED #####");
329:
330: return true;
331: }
332:
333: /**
334: * This method is a recursive method that searches for the first open
335: * object in the given object chain.
336: *
337: * @param next is the node who's children we will start with.
338: *
339: * @return the next open object.
340: */
341: public JXObject searchOpen(JXObject next) {
342: for (int i = 0; i < next.getLength(); i++) {
343: JXObject tmpObject = next.stack.elementAt(i);
344:
345: if (tmpObject.getState()) {
346: return searchOpen(tmpObject);
347: }
348: }
349:
350: return next;
351: }
352:
353: /**
354: * This method searches for all occurences of an element matching the
355: * given element and if a match is found, returns it. Otherwise null is
356: * returned.
357: *
358: * @param localName is the element to search for.
359: */
360: public JXObject searchOpenMatch(String localName) {
361: JXObject last = null;
362: JXObject match = null;
363:
364: last = this .searchOpen(this .stackObject);
365:
366: while (last != null) {
367: if (last.getTAG().equals(localName)) {
368: return last;
369: }
370:
371: last = last.getLast();
372: }
373:
374: return null;
375: }
376:
377: /**
378: * This method serializes the form to XML.
379: *
380: * @return true on success, else false on failure.
381: */
382: public boolean serializeForm() {
383: String systemId = this .getApplicationFile();
384:
385: systemId = systemId + ".tmp";
386:
387: StringWriter sw = new StringWriter();
388:
389: // Write the header.
390: sw.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
391: sw.flush();
392:
393: // Write the plugins.
394: sw.write("<!--\n");
395: sw.write("[JXPLUGIN_START]\n\n");
396:
397: Vector<Plugin> list = this .pluginParser.getPluginList();
398:
399: for (int i = 0; i < list.size(); i++) {
400: Plugin plugin = list.elementAt(i);
401: sw.write(plugin.getPluginName() + "="
402: + plugin.getPluginJar() + "\n");
403: }
404:
405: sw.write("[JXPLUGIN_END]\n");
406: sw.write("-->\n\n");
407: sw.flush();
408:
409: // Write the form.
410: this .serializeJXObject(this .stackObject, sw, 0);
411:
412: this .serializedForm = sw.toString();
413:
414: return true;
415: }
416:
417: /**
418: * This is a recursive utility method to serialize the form.
419: *
420: * @param jxObject is the object we want to serialize.
421: * @param sw is the string writer we want to use to serialize it.
422: * @param nl is the nest level of the XML structured tags. We use this to
423: * make the code pretty and indent.
424: *
425: * @return the object it has just serialized.
426: */
427: public JXObject serializeJXObject(JXObject jxObject,
428: StringWriter sw, int nl) {
429: boolean cdataFlag = false;
430: String cdata = "";
431: String tab = "";
432:
433: while (jxObject != null) {
434: Iterator k = jxObject.getPropertyList().keySet().iterator();
435: Iterator v = jxObject.getPropertyList().values().iterator();
436:
437: // Write Tag Start.
438: for (int x = 0; x < nl; x++) {
439: tab = tab + "\t";
440: }
441: sw.write(tab + "<");
442: sw.write(jxObject.getTAG() + " ");
443: sw.flush();
444:
445: // Write Tag Attributes.
446: while (k.hasNext()) {
447: String key = (String) k.next();
448: String value = (String) v.next();
449:
450: if (key.equals("cdata")) {
451: cdataFlag = true;
452: cdata = value;
453: } else {
454: sw.write(key + "=\"" + value + "\" ");
455: }
456: }
457:
458: sw.flush();
459:
460: // Write the Tag Close
461: if (jxObject.hasNodes() || cdataFlag == true) {
462: sw.write(">\n");
463: sw.flush();
464:
465: for (int i = 0; i < jxObject.getLength(); i++) {
466: this .serializeJXObject(jxObject.getElement(i), sw,
467: nl + 1);
468: }
469:
470: if (cdataFlag == true) {
471: sw.write(tab + "\t<![CDATA[\n" + cdata + "\n" + tab
472: + "\t]]>\n");
473: sw.flush();
474: }
475:
476: sw.write(tab + "</" + jxObject.getTAG() + ">\n");
477: sw.flush();
478: } else {
479: sw.write("/>\n");
480: sw.flush();
481: }
482:
483: break;
484: }
485:
486: return jxObject;
487: }
488:
489: /**
490: * This method associates an application file with the Virtual Machine.
491: *
492: * @param file points to the application file we want to use.
493: */
494: public void setApplicationFile(String file) {
495: this .applicationFile = file;
496: }
497:
498: /**
499: * This method associates a JXViewer instance with the Virtual Machine.
500: *
501: * @param jxViewer points to an instance of the JXViewer class.
502: */
503: public void setViewer(JXViewer jxViewer) {
504: this .jxViewer = jxViewer;
505: }
506:
507: /**
508: * This method calls the specified stack operation:
509: * <ul>
510: * <li>Stack Push - JXSTACK_PUSH, pushes data onto the stack.
511: * <li>Stack Update - JXSTACK_UPDATE, updates the stack storage area.
512: * <li>Stack Final - JXSTACK_FINAL, completes all operations on storage.
513: * </ul>
514: *
515: * @return true on success, else false on failure.
516: */
517: public boolean stackCall(String localName, String data,
518: Attributes attr, JXGLOBAL.JXSTACK_CALL call) {
519: switch (call) {
520: case JXSTACK_PUSH:
521: return stackPush(localName, attr);
522: case JXSTACK_UPDATE:
523: return stackUpdate(localName, data);
524: case JXSTACK_FINAL:
525: return stackFinal(localName);
526: default:
527: new JXMessageBox(JOptionPane.ERROR_MESSAGE,
528: "Unknown Stack Call",
529: "The stack call can not be determined", this
530: .getViewer());
531: System.exit(0);
532: }
533:
534: return false;
535: }
536:
537: /**
538: * This method is called when all operations on the specific stack item
539: * have been finalized.
540: *
541: * @param localName is the element name that we will use to reference the
542: * stack object. The element type must match and it must be on the "open
543: * route", meaning a stack push has occured bt no prior finalize has been
544: * called on the item.
545: *
546: * @return true on success, else false on failure.
547: */
548: public boolean stackFinal(String localName) {
549: if (JXGLOBAL.DEBUG_STACK)
550: System.out.println("##### STACK FINAL STARTED #####");
551:
552: JXObject tmpObject = this .searchOpenMatch(localName);
553:
554: if (tmpObject == null) {
555: new JXMessageBox(JOptionPane.ERROR_MESSAGE,
556: "Stack Element Does Not Exist",
557: "The stack element " + localName
558: + " does not exist in the stack!", this
559: .getViewer());
560: return false;
561: }
562:
563: tmpObject.setState(false);
564:
565: if (JXGLOBAL.DEBUG_STACK)
566: System.out.println("##### STACK FINAL COMPLETED #####");
567:
568: return true;
569: }
570:
571: /**
572: * This method is called when a new item must be added to the stack.
573: *
574: * @param localName is the element we would like to add to the stack.
575: * @param attr is the set of attributes for the specific element.
576: *
577: * @return true on success, else false on failure.
578: */
579: public boolean stackPush(String localName, Attributes attr) {
580: if (JXGLOBAL.DEBUG_STACK)
581: System.out.println("##### STACK PUSH STARTED #####");
582:
583: JXObject tmpObject = new JXObject(this , localName);
584:
585: if (attr.getValue("name") != null)
586: tmpObject.setName(attr.getValue("name"));
587: else {
588: tmpObject.setName(localName);
589: }
590:
591: for (int i = 0; i < attr.getLength(); i++) {
592: String attribute = attr.getLocalName(i);
593: String value = attr.getValue(i);
594:
595: tmpObject.setProperty(attribute, value);
596: }
597:
598: if (this .stackObject == null) {
599: this .stackObject = tmpObject;
600: } else {
601: this .searchOpen(this .stackObject).appendNode(tmpObject);
602: }
603:
604: if (JXGLOBAL.DEBUG_STACK)
605: System.out.println("##### STACK PUSH COMPLETED #####");
606:
607: return true;
608: }
609:
610: /**
611: * This method is called when a object on the stack needs to be updated.
612: * It can not be called once a stack object has been finalized. Currently
613: * objects are only update when they are a jxscript or jximage.
614: *
615: * @param localName is the element we would like to update.
616: * @param data is the data behind the attribute we want to update.
617: *
618: * @return true on success, else false on failure.
619: */
620: public boolean stackUpdate(String localName, String data) {
621: if (JXGLOBAL.DEBUG_STACK)
622: System.out.println("##### STACK UPDATE STARTED #####");
623:
624: JXObject tmpObject = this .searchOpenMatch(localName);
625:
626: if (tmpObject == null) {
627: new JXMessageBox(JOptionPane.ERROR_MESSAGE,
628: "Stack Element Does Not Exist",
629: "The stack element " + localName
630: + " does not exist in the stack!", this
631: .getViewer());
632: return false;
633: }
634:
635: if (data.equals("") == false)
636: tmpObject.setProperty("cdata", data);
637:
638: if (JXGLOBAL.DEBUG_STACK)
639: System.out.println("##### STACK UPDATE COMPLETED #####");
640:
641: return true;
642: }
643:
644: }
|