001: /*
002: * SwingML Copyright (C) 2002 SwingML Team This library is free software; you
003: * can redistribute it and/or modify it under the terms of the GNU Lesser
004: * General Public License as published by the Free Software Foundation; either
005: * version 2 of the License, or (at your option) any later version. This library
006: * is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
007: * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
008: * PARTICULAR PURPOSE.
009: *
010: * See the GNU Lesser General Public License for more details. You should have
011: * received a copy of the GNU Lesser General Public License along with this
012: * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place -
013: * Suite 330, Boston, MA 02111-1307, USA. Authors: Ezequiel Cuellar
014: * <ecuellar@crosslogic.com> Robert Morris <robertj@morris.net> Alessandro
015: * Dibella <alessandro.dibella@fuurou.org>
016: */
017: package org.swingml;
018:
019: import java.awt.*;
020: import java.awt.event.*;
021: import java.lang.reflect.*;
022: import java.net.*;
023: import java.util.*;
024: import java.util.List;
025:
026: import javax.swing.*;
027:
028: import org.swingml.action.*;
029: import org.swingml.component.*;
030: import org.swingml.errors.*;
031: import org.swingml.errors.handlers.*;
032: import org.swingml.event.*;
033: import org.swingml.registry.*;
034: import org.swingml.server.*;
035: import org.swingml.system.*;
036: import org.swingml.view.*;
037: import org.swingml.xml.*;
038: import org.w3c.dom.*;
039:
040: /**
041: * This class represents the heart of the SwingML system. Use this class as
042: * either an application or applet. The two most significant methods are the
043: * submit() and render() methods.
044: *
045: * @author <a href="mailto:ecuellar@crosslogic.com">Ezequiel Cuellar</a>
046: * @author <a href="mailto:robertj@morris.net">Robert J. Morris</a>
047: */
048: public class SwingMLRenderer extends JApplet {
049:
050: private static boolean isRendering = false;
051: protected static SwingMLRenderer renderer = null;
052: private static List renderingListeners = new ArrayList();
053: private static SwingMLDesktop theFrame = null;
054: private static String windowIconURL;
055:
056: public static void addRenderingListener(IRenderListener aListener) {
057: if (!getRenderingListeners().contains(aListener)) {
058: getRenderingListeners().add(aListener);
059: }
060: }
061:
062: static void endRendering(Node xmlNode) {
063: setRendering(false);
064: notifyRenderingEnd(xmlNode);
065: }
066:
067: public static SwingMLDesktop getDesktop() {
068: return theFrame;
069: }
070:
071: public static SwingMLRenderer getRenderer() {
072: if (renderer == null) {
073: renderer = new SwingMLRenderer();
074: }
075: return renderer;
076: }
077:
078: public static List getRenderingListeners() {
079: return renderingListeners;
080: }
081:
082: public static String getWindowIconURL() {
083: return windowIconURL;
084: }
085:
086: public static boolean isRendering() {
087: return isRendering;
088: }
089:
090: /**
091: * When this class is used as the entry point for an application. This
092: * method expects that the 0th element in the args[] parameter contains the
093: * URL of the SwingML document to render.
094: */
095: public static void main(String args[])
096: throws SwingMLInitializationError {
097: if (args.length != 0) {
098: theFrame = new SwingMLDesktop();
099:
100: theFrame.addWindowListener(new WindowAdapter() {
101:
102: public void windowClosing(WindowEvent e) {
103: System.exit(0);
104: ExternalEventManager.flush();
105: }
106: });
107: try {
108: theFrame.setSize(Integer.parseInt(args[1]), Integer
109: .parseInt(args[2]));
110: } catch (Exception e) {
111: theFrame.setSize(550, 500);
112: }
113: Dimension theScreenSize = Toolkit.getDefaultToolkit()
114: .getScreenSize();
115: Dimension theFrameSize = theFrame.getSize();
116: int theHorizontalPosition = (int) (theScreenSize.getWidth() - theFrameSize
117: .getWidth()) / 2;
118: int theVerticalPosition = (int) (theScreenSize.getHeight() - theFrameSize
119: .getHeight()) / 2;
120: theFrame.setLocation(theHorizontalPosition,
121: theVerticalPosition);
122: // set code base if supplied
123: if (args.length > 3) {
124: try {
125: HttpSubmitController.setCodeBase(new URL(args[3]));
126: } catch (MalformedURLException x) {
127: SwingMLLogger.getInstance().log(
128: "Error setting code base: " + args[3]);
129: }
130: }
131: if (args.length > 4) {
132: theFrame.setTitle(args[4]);
133: }
134:
135: HttpSubmitController.processDocumentBase(args[0]);
136: if (HttpSubmitController.getServerURL() != null) {
137: theFrame.getContentPane().add(getRenderer());
138: SwingMLServerResponse response = HttpSubmitController
139: .getSpec(args[0], getRenderer()
140: .getContentPane());
141: if (response != null) {
142: getRenderer().render(response.getSwingMLSpec(),
143: getRenderer().getContentPane(), true);
144: getRenderer().updateContentPane();
145: if (!response.hasErrors()) {
146: if (args.length > 5) {
147: if (args[5].toUpperCase().startsWith("MAX")) {
148: theFrame
149: .setExtendedState(Frame.MAXIMIZED_BOTH);
150: }
151: }
152:
153: if (args.length > 6) {
154: setWindowIconURL(args[6]);
155: theFrame.setIconImage(IconFactory
156: .getIconImage(getWindowIconURL()));
157: }
158:
159: theFrame.setPropsDirectoryName(SwingMLLogger
160: .getLogDirectory());
161: if (theFrame.hasSettings()) {
162: theFrame.applySettings();
163: }
164:
165: theFrame.setVisible(true);
166: } else {
167: // iterate errors, building up a message and throw an Initialization error.
168: String message = "There was an error initializing the application.";
169: ISwingMLError errors[] = response.getErrors();
170: if (errors != null) {
171: for (int x = 0; x < errors.length; x++) {
172: ISwingMLError error = errors[x];
173: message += "\n" + error.getText();
174: }
175: }
176: throw new SwingMLInitializationError(message);
177: }
178: } else {
179: throw new SwingMLInitializationError(
180: "Unable to connect to the server");
181: }
182: }
183: } else {
184: SwingMLLogger.getInstance().log(
185: "Specification URL missing.");
186: }
187: }
188:
189: private static void notifyRenderingEnd(Node xmlNode) {
190: List listeners = new ArrayList(getRenderingListeners());
191: Iterator schmiterator = listeners.iterator();
192: while (schmiterator.hasNext()) {
193: // Only notify listeners listening for components inside this
194: // container, or those listening for all components.
195: boolean shouldBeNotified = true;
196: IRenderListener listener = (IRenderListener) schmiterator
197: .next();
198: if (listener.getComponentName() != null) {
199: shouldBeNotified = SwingMLComponentUtilities
200: .nodeContains(xmlNode, listener
201: .getComponentName());
202: }
203:
204: if (shouldBeNotified) {
205: listener.renderingEnded();
206: }
207: }
208: }
209:
210: private static void notifyRenderingStart(Node xmlNode) {
211: List listeners = new ArrayList(getRenderingListeners());
212: Iterator schmiterator = listeners.iterator();
213: while (schmiterator.hasNext()) {
214: // Only notify listeners listening for components inside this
215: // container, or those listening for all components.
216: boolean shouldBeNotified = true;
217: IRenderListener listener = (IRenderListener) schmiterator
218: .next();
219: if (listener.getComponentName() != null) {
220: shouldBeNotified = SwingMLComponentUtilities
221: .nodeContains(xmlNode, listener
222: .getComponentName());
223: }
224:
225: if (shouldBeNotified) {
226: listener.renderingStarted();
227: }
228:
229: }
230: }
231:
232: public static void removeRenderingListener(Object aListener) {
233: if (getRenderingListeners().contains(aListener)) {
234: getRenderingListeners().remove(aListener);
235: }
236: }
237:
238: protected static void setRenderer(SwingMLRenderer aRenderer) {
239: renderer = aRenderer;
240: }
241:
242: private static void setRendering(boolean rendering) {
243: SwingMLRenderer.isRendering = rendering;
244: }
245:
246: public static void setWindowIconURL(String windowIconURL) {
247: SwingMLRenderer.windowIconURL = windowIconURL;
248: }
249:
250: private static void startRendering(Node rootNode) {
251: setRendering(true);
252: notifyRenderingStart(rootNode);
253: }
254:
255: public SwingMLRenderer() {
256:
257: }
258:
259: public void destroy() {
260: ExternalEventManager.flush(this );
261: }
262:
263: /**
264: * Executes the given action.
265: *
266: * @param response
267: * @param action
268: * @param aContainer
269: */
270: public void executeRemoteAction(SwingMLServerResponse response,
271: RemoteAction action, Container aContainer) {
272: if (response != null && response.getSwingMLSpec() != null
273: && response.getSwingMLSpec().length() > 0) {
274: try {
275: ExternalEventManager.flush(this );
276: MapperUtil theMapperUtil = new MapperUtil();
277: Node theNode = theMapperUtil.parse(response
278: .getSwingMLSpec());
279: action.parseResponse(theNode);
280: } catch (Exception e) {
281: // Execution of remote action failed.
282: // TODO - Should we render a dialog here? Probably so.
283: SwingMLLogger.getInstance().log(SwingMLLogger.ERROR,
284: e.getMessage());
285: e.printStackTrace();
286: }
287: }
288: }
289:
290: public URL getCodeBase() {
291: return HttpSubmitController.getCodeBase();
292: }
293:
294: public URL getDocumentBase() {
295: URL result = null;
296: try {
297: result = new URL(HttpSubmitController.getServerURL());
298: } catch (MalformedURLException e) {
299: SwingMLLogger.getInstance().log(SwingMLLogger.ERROR, e);
300: }
301: return result;
302: }
303:
304: /**
305: * Parse the given xml and return the root node.
306: *
307: * @param xml
308: * @return Node - the root node
309: */
310: public Node getDocumentNode(String xml) {
311: Node result = null;
312:
313: try {
314: MapperUtil theMapperUtil = new MapperUtil();
315: result = theMapperUtil.parse(xml);
316: } catch (Exception e) {
317: result = null;
318: SwingMLLogger.getInstance().log(e);
319: }
320:
321: return result;
322: }
323:
324: /**
325: * Called when a SwingMLRenderer instance is displayed or run as an applet.
326: */
327: public void init() {
328: HttpSubmitController.setCodeBase(super .getCodeBase());
329: String theSpecLocation = getParameter("SPEC");
330:
331: HttpSubmitController.processDocumentBase(theSpecLocation);
332: if (this .getDocumentBase() != null) {
333: SwingMLServerResponse response = HttpSubmitController
334: .getSpec(theSpecLocation, this .getContentPane());
335: if (response != null) {
336: render(response.getSwingMLSpec(), getContentPane());
337: if (response.hasErrors()) {
338: render(ErrorHandlerUtilities.getDialogFor(response
339: .getErrors()), getContentPane());
340: }
341: } else {
342: render(HttpSubmitController.getConnectionErrorSpec(),
343: getContentPane());
344: }
345: }
346: }
347:
348: public void processAndRender(String xml,
349: Container containerToReplace, boolean repaint) {
350: processAndRender(xml, containerToReplace, repaint, false);
351: }
352:
353: /**
354: * **NOTE** - Render the new components and then remove their old counterparts afterwards.
355: * This reduces the flashing on screen caused by first removing components and then re-rendering them.
356: * @param xml
357: * @param containerToReplace
358: * @param repaint
359: * @param renderImmediately
360: */
361: public void processAndRender(String xml,
362: Container containerToReplace, boolean repaint,
363: boolean renderImmediately) {
364: if (xml != null && xml.length() > 0) {
365: Container parent = null;
366: if (repaint) {
367: parent = containerToReplace.getParent();
368: if (parent != null) {
369: parent.remove(containerToReplace);
370: AbstractSwingMLModel replaceModel = SwingMLModelToContainerRegistry
371: .getModel(containerToReplace);
372: AbstractSwingMLModel parentModel = replaceModel
373: .getParent();
374: parentModel.removeChild(replaceModel);
375: replaceModel
376: .unregister(replaceModel.getContainer());
377:
378: }
379: }
380:
381: render(xml, parent, renderImmediately);
382: }
383: }
384:
385: /**
386: * Screens should be rendered on background thread as to not lock up the UI.
387: *
388: * @param xml
389: * @param aContainer
390: */
391: public void render(String aSpec, Container aContainer) {
392: render(aSpec, aContainer, false);
393: }
394:
395: public void render(final String xml, final Container parent,
396: boolean renderImmediately) {
397: if (xml != null && xml.length() > 0) {
398: ExternalEventManager.flush(this );
399:
400: // FIXME - This needs to be refactored to find the appropriate parent model and then iterate/render the new model we map from the XML
401: final SwingMLModel bogus = new SwingMLModel();
402: MapperUtil theMapperUtil = new MapperUtil();
403: final Node theNode = getDocumentNode(xml);
404: theMapperUtil.iterate(theNode, bogus, parent);
405:
406: // Create a runnable for the rendering portion, in case it needs to
407: // be done in the background.
408: Runnable bgRenderer = new Runnable() {
409:
410: public void run() {
411: // Perform the render
412: RendererUtil theRendererUtil = new RendererUtil();
413: theRendererUtil.iterate(bogus, parent);
414:
415: // Update the UI
416: if (parent != null) {
417: JComponent theContainer = (JComponent) parent;
418: theContainer.updateUI();
419: }
420:
421: // Notify listeners of rendering completion.
422: endRendering(theNode);
423: }
424: };
425:
426: // Notify listeners and start rendering
427: startRendering(theNode);
428: if (renderImmediately) {
429: try {
430: SwingUtilities.invokeAndWait(bgRenderer);
431: } catch (InterruptedException e) {
432: SwingMLLogger.getInstance().log(e);
433: } catch (InvocationTargetException e) {
434: SwingMLLogger.getInstance().log(e);
435: }
436: } else {
437: SwingUtilities.invokeLater(bgRenderer);
438: }
439: }
440: }
441:
442: public void updateContentPane() {
443: Container contentPane = getContentPane();
444: StatusBar statusBar = new StatusBar(SwingMLRenderer.this);
445: StatusBar.register(statusBar, contentPane);
446: contentPane.add(statusBar.getBar(), BorderLayout.SOUTH);
447: }
448: }
|