/*
* SwingML Copyright (C) 2002 SwingML Team This library is free software; you
* can redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version. This library
* is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE.
*
* See the GNU Lesser General Public License for more details. You should have
* received a copy of the GNU Lesser General Public License along with this
* library; if not, write to the Free Software Foundation, Inc., 59 Temple Place -
* Suite 330, Boston, MA 02111-1307, USA. Authors: Ezequiel Cuellar
* <ecuellar@crosslogic.com> Robert Morris <robertj@morris.net> Alessandro
* Dibella <alessandro.dibella@fuurou.org>
*/
package org.swingml;
import java.awt.*;
import java.awt.event.*;
import java.lang.reflect.*;
import java.net.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import org.swingml.action.*;
import org.swingml.component.*;
import org.swingml.errors.*;
import org.swingml.errors.handlers.*;
import org.swingml.event.*;
import org.swingml.registry.*;
import org.swingml.server.*;
import org.swingml.system.*;
import org.swingml.view.*;
import org.swingml.xml.*;
import org.w3c.dom.*;
/**
* This class represents the heart of the SwingML system. Use this class as
* either an application or applet. The two most significant methods are the
* submit() and render() methods.
*
* @author <a href="mailto:ecuellar@crosslogic.com">Ezequiel Cuellar</a>
* @author <a href="mailto:robertj@morris.net">Robert J. Morris</a>
*/
public class SwingMLRenderer extends JApplet {
private static boolean isRendering = false;
protected static SwingMLRenderer renderer = null;
private static List renderingListeners = new ArrayList();
private static SwingMLDesktop theFrame = null;
private static String windowIconURL;
public static void addRenderingListener (IRenderListener aListener) {
if (!getRenderingListeners().contains(aListener)) {
getRenderingListeners().add(aListener);
}
}
static void endRendering (Node xmlNode) {
setRendering(false);
notifyRenderingEnd(xmlNode);
}
public static SwingMLDesktop getDesktop () {
return theFrame;
}
public static SwingMLRenderer getRenderer () {
if (renderer == null) {
renderer = new SwingMLRenderer();
}
return renderer;
}
public static List getRenderingListeners () {
return renderingListeners;
}
public static String getWindowIconURL () {
return windowIconURL;
}
public static boolean isRendering () {
return isRendering;
}
/**
* When this class is used as the entry point for an application. This
* method expects that the 0th element in the args[] parameter contains the
* URL of the SwingML document to render.
*/
public static void main (String args[]) throws SwingMLInitializationError {
if (args.length != 0) {
theFrame = new SwingMLDesktop();
theFrame.addWindowListener(new WindowAdapter() {
public void windowClosing (WindowEvent e) {
System.exit(0);
ExternalEventManager.flush();
}
});
try {
theFrame.setSize(Integer.parseInt(args[1]), Integer.parseInt(args[2]));
} catch (Exception e) {
theFrame.setSize(550, 500);
}
Dimension theScreenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension theFrameSize = theFrame.getSize();
int theHorizontalPosition = (int) (theScreenSize.getWidth() - theFrameSize.getWidth()) / 2;
int theVerticalPosition = (int) (theScreenSize.getHeight() - theFrameSize.getHeight()) / 2;
theFrame.setLocation(theHorizontalPosition, theVerticalPosition);
// set code base if supplied
if (args.length > 3) {
try {
HttpSubmitController.setCodeBase(new URL(args[3]));
} catch (MalformedURLException x) {
SwingMLLogger.getInstance().log("Error setting code base: " + args[3]);
}
}
if (args.length > 4) {
theFrame.setTitle(args[4]);
}
HttpSubmitController.processDocumentBase(args[0]);
if (HttpSubmitController.getServerURL() != null) {
theFrame.getContentPane().add(getRenderer());
SwingMLServerResponse response = HttpSubmitController.getSpec(args[0], getRenderer().getContentPane());
if (response != null) {
getRenderer().render(response.getSwingMLSpec(), getRenderer().getContentPane(), true);
getRenderer().updateContentPane();
if (!response.hasErrors()) {
if (args.length > 5) {
if (args[5].toUpperCase().startsWith("MAX")) {
theFrame.setExtendedState(Frame.MAXIMIZED_BOTH);
}
}
if (args.length > 6) {
setWindowIconURL(args[6]);
theFrame.setIconImage(IconFactory.getIconImage(getWindowIconURL()));
}
theFrame.setPropsDirectoryName(SwingMLLogger.getLogDirectory());
if (theFrame.hasSettings()) {
theFrame.applySettings();
}
theFrame.setVisible(true);
} else {
// iterate errors, building up a message and throw an Initialization error.
String message = "There was an error initializing the application.";
ISwingMLError errors[] = response.getErrors();
if (errors != null) {
for (int x = 0; x < errors.length; x++) {
ISwingMLError error = errors[x];
message += "\n" + error.getText();
}
}
throw new SwingMLInitializationError(message);
}
} else {
throw new SwingMLInitializationError("Unable to connect to the server");
}
}
} else {
SwingMLLogger.getInstance().log("Specification URL missing.");
}
}
private static void notifyRenderingEnd (Node xmlNode) {
List listeners = new ArrayList(getRenderingListeners());
Iterator schmiterator = listeners.iterator();
while (schmiterator.hasNext()) {
// Only notify listeners listening for components inside this
// container, or those listening for all components.
boolean shouldBeNotified = true;
IRenderListener listener = (IRenderListener) schmiterator.next();
if (listener.getComponentName() != null) {
shouldBeNotified = SwingMLComponentUtilities.nodeContains(xmlNode, listener.getComponentName());
}
if (shouldBeNotified) {
listener.renderingEnded();
}
}
}
private static void notifyRenderingStart (Node xmlNode) {
List listeners = new ArrayList(getRenderingListeners());
Iterator schmiterator = listeners.iterator();
while (schmiterator.hasNext()) {
// Only notify listeners listening for components inside this
// container, or those listening for all components.
boolean shouldBeNotified = true;
IRenderListener listener = (IRenderListener) schmiterator.next();
if (listener.getComponentName() != null) {
shouldBeNotified = SwingMLComponentUtilities.nodeContains(xmlNode, listener.getComponentName());
}
if (shouldBeNotified) {
listener.renderingStarted();
}
}
}
public static void removeRenderingListener (Object aListener) {
if (getRenderingListeners().contains(aListener)) {
getRenderingListeners().remove(aListener);
}
}
protected static void setRenderer (SwingMLRenderer aRenderer) {
renderer = aRenderer;
}
private static void setRendering (boolean rendering) {
SwingMLRenderer.isRendering = rendering;
}
public static void setWindowIconURL (String windowIconURL) {
SwingMLRenderer.windowIconURL = windowIconURL;
}
private static void startRendering (Node rootNode) {
setRendering(true);
notifyRenderingStart(rootNode);
}
public SwingMLRenderer () {
}
public void destroy () {
ExternalEventManager.flush(this);
}
/**
* Executes the given action.
*
* @param response
* @param action
* @param aContainer
*/
public void executeRemoteAction (SwingMLServerResponse response, RemoteAction action, Container aContainer) {
if (response != null && response.getSwingMLSpec() != null && response.getSwingMLSpec().length() > 0) {
try {
ExternalEventManager.flush(this);
MapperUtil theMapperUtil = new MapperUtil();
Node theNode = theMapperUtil.parse(response.getSwingMLSpec());
action.parseResponse(theNode);
} catch (Exception e) {
// Execution of remote action failed.
// TODO - Should we render a dialog here? Probably so.
SwingMLLogger.getInstance().log(SwingMLLogger.ERROR, e.getMessage());
e.printStackTrace();
}
}
}
public URL getCodeBase () {
return HttpSubmitController.getCodeBase();
}
public URL getDocumentBase () {
URL result = null;
try {
result = new URL(HttpSubmitController.getServerURL());
} catch (MalformedURLException e) {
SwingMLLogger.getInstance().log(SwingMLLogger.ERROR, e);
}
return result;
}
/**
* Parse the given xml and return the root node.
*
* @param xml
* @return Node - the root node
*/
public Node getDocumentNode (String xml) {
Node result = null;
try {
MapperUtil theMapperUtil = new MapperUtil();
result = theMapperUtil.parse(xml);
} catch (Exception e) {
result = null;
SwingMLLogger.getInstance().log(e);
}
return result;
}
/**
* Called when a SwingMLRenderer instance is displayed or run as an applet.
*/
public void init () {
HttpSubmitController.setCodeBase(super.getCodeBase());
String theSpecLocation = getParameter("SPEC");
HttpSubmitController.processDocumentBase(theSpecLocation);
if (this.getDocumentBase() != null) {
SwingMLServerResponse response = HttpSubmitController.getSpec(theSpecLocation, this.getContentPane());
if (response != null) {
render(response.getSwingMLSpec(), getContentPane());
if (response.hasErrors()) {
render(ErrorHandlerUtilities.getDialogFor(response.getErrors()), getContentPane());
}
} else {
render(HttpSubmitController.getConnectionErrorSpec(), getContentPane());
}
}
}
public void processAndRender (String xml, Container containerToReplace, boolean repaint) {
processAndRender(xml, containerToReplace, repaint, false);
}
/**
* **NOTE** - Render the new components and then remove their old counterparts afterwards.
* This reduces the flashing on screen caused by first removing components and then re-rendering them.
* @param xml
* @param containerToReplace
* @param repaint
* @param renderImmediately
*/
public void processAndRender (String xml, Container containerToReplace, boolean repaint, boolean renderImmediately) {
if (xml != null && xml.length() > 0) {
Container parent = null;
if (repaint) {
parent = containerToReplace.getParent();
if (parent != null) {
parent.remove(containerToReplace);
AbstractSwingMLModel replaceModel = SwingMLModelToContainerRegistry.getModel(containerToReplace);
AbstractSwingMLModel parentModel = replaceModel.getParent();
parentModel.removeChild(replaceModel);
replaceModel.unregister(replaceModel.getContainer());
}
}
render(xml, parent, renderImmediately);
}
}
/**
* Screens should be rendered on background thread as to not lock up the UI.
*
* @param xml
* @param aContainer
*/
public void render (String aSpec, Container aContainer) {
render(aSpec, aContainer, false);
}
public void render (final String xml, final Container parent, boolean renderImmediately) {
if (xml != null && xml.length() > 0) {
ExternalEventManager.flush(this);
// FIXME - This needs to be refactored to find the appropriate parent model and then iterate/render the new model we map from the XML
final SwingMLModel bogus = new SwingMLModel();
MapperUtil theMapperUtil = new MapperUtil();
final Node theNode = getDocumentNode(xml);
theMapperUtil.iterate(theNode, bogus, parent);
// Create a runnable for the rendering portion, in case it needs to
// be done in the background.
Runnable bgRenderer = new Runnable() {
public void run () {
// Perform the render
RendererUtil theRendererUtil = new RendererUtil();
theRendererUtil.iterate(bogus, parent);
// Update the UI
if (parent != null) {
JComponent theContainer = (JComponent) parent;
theContainer.updateUI();
}
// Notify listeners of rendering completion.
endRendering(theNode);
}
};
// Notify listeners and start rendering
startRendering(theNode);
if (renderImmediately) {
try {
SwingUtilities.invokeAndWait(bgRenderer);
} catch (InterruptedException e) {
SwingMLLogger.getInstance().log(e);
} catch (InvocationTargetException e) {
SwingMLLogger.getInstance().log(e);
}
} else {
SwingUtilities.invokeLater(bgRenderer);
}
}
}
public void updateContentPane () {
Container contentPane = getContentPane();
StatusBar statusBar = new StatusBar(SwingMLRenderer.this);
StatusBar.register(statusBar, contentPane);
contentPane.add(statusBar.getBar(), BorderLayout.SOUTH);
}
}
|