123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767 |
- /* *************************************************************************
-
- Enably Toolkit
-
- Development of Browser User Intarfaces Made Easy
-
- Copyright (C) 2000-2006 IT Mill Ltd
-
- *************************************************************************
-
- This product is distributed under commercial license that can be found
- from the product package on license/license.txt. Use of this product might
- require purchasing a commercial license from IT Mill Ltd. For guidelines
- on usage, see license/licensing-guidelines.html
-
- *************************************************************************
-
- For more information, contact:
-
- IT Mill Ltd phone: +358 2 4802 7180
- Ruukinkatu 2-4 fax: +358 2 4802 7181
- 20540, Turku email: info@enably.com
- Finland company www: www.enably.com
-
- Primary source for information and releases: www.enably.com
-
- ********************************************************************** */
-
- package com.enably.tk;
-
- import com.enably.tk.service.ApplicationContext;
- import com.enably.tk.terminal.*;
- import com.enably.tk.ui.AbstractComponent;
- import com.enably.tk.ui.Window;
-
- import java.util.Collection;
- import java.util.Collections;
- import java.util.Enumeration;
- import java.util.EventListener;
- import java.util.EventObject;
- import java.util.Hashtable;
- import java.util.Iterator;
- import java.util.LinkedList;
- import java.util.Locale;
- import java.util.Properties;
- import java.util.Random;
- import java.net.MalformedURLException;
- import java.net.URL;
-
- /** <p>Abstract base class required for all MillStone applications. This
- * class provides all the basic services required by the MillStone
- * framework. These services allow external discovery and manipulation of
- * the user, {@link com.enably.tk.ui.Window windows} and
- * themes, and starting and stopping the application.</p>
- *
- * <p>As mentioned, all MillStone applications must inherit this class.
- * However, this is almost all of what one needs to do to create a fully
- * functional MillStone application. The only thing a class inheriting the
- * <code>Application</code> needs to do is implement the <code>init()</code>
- * where it creates the windows it needs to perform its function. Note that
- * all MillStone applications must have at least one window: the main
- * window. The first unnamed window constructed by an application
- * automatically becomes the main window which behaves just like other
- * windows with one exception: when accessing windows using URLs the main
- * window corresponds to the application URL whereas other windows
- * correspond to a URL gotten by catenating the window's name to the
- * application URL.</p>
- *
- * <p>See the class <code>com.enably.tk.demo.HelloWorld</code> for
- * a simple example of a fully working MillStone Application.</p>
- *
- * <p><strong>Window access.</strong> <code>Application</code> provides
- * methods to list, add and remove the windows it contains.</p>
- *
- * <p><strong>Execution control.</strong> This class includes method to
- * start and finish the execution of the application. Being finished means
- * basically that no windows will be available from the application
- * anymore.</p>
- *
- * <p><strong>Theme selection.</strong> The MillStone theme selection
- * process allows a theme to be specified at three different levels. When
- * a window's theme needs to be found out, the window itself is queried
- * for a preferred theme. If the window does not prefer a specific theme,
- * the application containing the window is queried. If neither the
- * application prefers a theme, the default theme for the
- * {@link com.enably.tk.terminal.Terminal terminal} is used. The
- * terminal always defines a default theme.</p>
- *
- * @author IT Mill Ltd.
- * @version @VERSION@
- * @since 3.0
- */
- public abstract class Application
- implements URIHandler, Terminal.ErrorListener {
-
- /** Random window name generator */
- private static Random nameGenerator = new Random();
-
- /** Application context the application is running in */
- private ApplicationContext context;
-
- /** The current user or <code>null</code> if no user has logged in. */
- private Object user;
-
- /** Mapping from window name to window instance */
- private Hashtable windows = new Hashtable();
-
- /** Main window of the application. */
- private Window mainWindow = null;
-
- /** The application's URL. */
- private URL applicationUrl;
-
- /** Name of the theme currently used by the application. */
- private String theme = null;
-
- /** Application status */
- private boolean applicationIsRunning = false;
-
- /** Application properties */
- private Properties properties;
-
- /** Default locale of the application. */
- private Locale locale;
-
- /** List of listeners listening user changes */
- private LinkedList userChangeListeners = null;
-
- /** Window attach listeners */
- private LinkedList windowAttachListeners = null;
-
- /** Window detach listeners */
- private LinkedList windowDetachListeners = null;
-
- /** Application error listeners */
- private LinkedList errorListeners = null;
-
- /** Application resource mapping: key <-> resource */
- private Hashtable resourceKeyMap = new Hashtable();
- private Hashtable keyResourceMap = new Hashtable();
- private long lastResourceKeyNumber = 0;
-
- /** URL the user is redirected to on application close or
- * null if application is just closed */
- private String logoutURL = null;
-
- /** Gets a window by name. Returns <code>null</code>
- * if the application is not running or it does not contain a window
- * corresponding to <code>name</code>.
- *
- * @param name The name of the window.
- * @return The window associated with the given URI or <code>null</code>
- */
- public Window getWindow(String name) {
-
- // For closed app, do not give any windows
- if (!isRunning())
- return null;
-
- // Get the window by name
- Window window = (Window) windows.get(name);
-
- return window;
- }
-
- /** Adds a new window to the application.
- *
- * <p>This implicitly invokes the
- * {@link com.enably.tk.ui.Window#setApplication(Application)}
- * method. </p>
- *
- * @param window the new <code>Window</code> to add
- * @throws IllegalArgumentException if a window with the same name
- * as the new window already exists in the application
- * @throws NullPointerException if the given <code>Window</code> or
- * its name is <code>null</code>
- */
- public void addWindow(Window window)
- throws IllegalArgumentException, NullPointerException {
-
- // Nulls can not be added to application
- if (window == null)
- return;
-
- // Get the naming proposal from window
- String name = window.getName();
-
- // Check that the application does not already contain
- // window having the same name
- if (name != null && windows.containsKey(name)) {
-
- // If the window is already added
- if (window == windows.get(name))
- return;
-
- // Otherwise complain
- throw new IllegalArgumentException(
- "Window with name '"
- + window.getName()
- + "' is already present in the application");
- }
-
- // If the name of the window is null, the window is automatically named
- if (name == null) {
- boolean accepted = false;
- while (!accepted) {
-
- // Try another name
- name = String.valueOf(Math.abs(nameGenerator.nextInt()));
- if (!windows.containsKey(name))
- accepted = true;
- }
- window.setName(name);
- }
-
- // Add the window to application
- windows.put(name, window);
- window.setApplication(this);
-
- // Fire window attach event
- if (windowAttachListeners != null) {
- Object[] listeners = windowAttachListeners.toArray();
- WindowAttachEvent event = new WindowAttachEvent(window);
- for (int i = 0; i < listeners.length; i++) {
- ((WindowAttachListener) listeners[i]).windowAttached(event);
- }
- }
-
- // If no main window is set, declare the window to be main window
- if (getMainWindow() == null)
- setMainWindow(window);
- }
-
- /** Removes the specified window from the application.
- *
- * @param window The window to be removed
- */
- public void removeWindow(Window window) {
- if (window != null && windows.contains(window)) {
-
- // Remove window from application
- windows.remove(window.getName());
-
- // If the window was main window, clear it
- if (getMainWindow() == window)
- setMainWindow(null);
-
- // Remove application from window
- if (window.getApplication() == this)
- window.setApplication(null);
-
- // Fire window detach event
- if (windowDetachListeners != null) {
- Object[] listeners = windowDetachListeners.toArray();
- WindowDetachEvent event = new WindowDetachEvent(window);
- for (int i = 0; i < listeners.length; i++) {
- ((WindowDetachListener) listeners[i]).windowDetached(event);
- }
- }
- }
- }
-
- /** Gets the user of the application.
- *
- * @return User of the application.
- */
- public Object getUser() {
- return user;
- }
-
- /** Sets the user of the application instance. An application instance
- * may have a user associated to it. This can be set in login procedure
- * or application initialization. A component performing the user login
- * procedure can assign the user property of the application and make
- * the user object available to other components of the application.
- *
- * @param user the new user.
- */
- public void setUser(Object user) {
- Object prevUser = this.user;
- if (user != prevUser && (user == null || !user.equals(prevUser))) {
- this.user = user;
- if (userChangeListeners != null) {
- Object[] listeners = userChangeListeners.toArray();
- UserChangeEvent event =
- new UserChangeEvent(this, user, prevUser);
- for (int i = 0; i < listeners.length; i++) {
- ((UserChangeListener) listeners[i]).applicationUserChanged(
- event);
- }
- }
- }
- }
-
- /** Gets the URL of the application.
- *
- * @return the application's URL.
- */
- public URL getURL() {
- return applicationUrl;
- }
-
- /** Ends the Application. In effect this will cause the application stop
- * returning any windows when asked.
- */
- public void close() {
- applicationIsRunning = false;
- }
-
- /** Starts the application on the given URL. After this call the
- * application corresponds to the given URL and it will return windows
- * when asked for them.
- *
- * @param applicationUrl The URL the application should respond to
- * @param applicationProperties Application properties as specified by the adapter.
- * @param context The context application will be running in
- *
- */
- public void start(URL applicationUrl, Properties applicationProperties, ApplicationContext context) {
- this.applicationUrl = applicationUrl;
- this.properties = applicationProperties;
- this.context = context;
- init();
- applicationIsRunning = true;
- }
-
- /** Tests if the application is running or if it has it been finished.
- *
- * @return <code>true</code> if the application is running,
- * <code>false</code> if not
- */
- public boolean isRunning() {
- return applicationIsRunning;
- }
-
- /** Gets the set of windows contained by the application.
- *
- * @return Unmodifiable collection of windows
- */
- public Collection getWindows() {
- return Collections.unmodifiableCollection(windows.values());
- }
-
- /** Main initializer of the application. This method is called by the
- * framework when the application is started, and it should perform
- * whatever initialization operations the application needs, such
- * as creating windows and adding components to them.
- */
- public abstract void init();
-
- /** Gets the application's theme. The application's theme is the default
- * theme used by all the windows in it that do not explicitly specify a
- * theme. If the application theme is not explicitly set, the
- * <code>null</code> is returned.
- *
- * @return the name of the application's theme
- */
- public String getTheme() {
- return theme;
- }
-
- /** Sets the application's theme. Note that this theme can be overridden
- * by the windows. <code>null</code> implies the default terminal theme.
- *
- * @param theme The new theme for this application
- */
- public void setTheme(String theme) {
-
- // Collect list of windows not having the current or future theme
- LinkedList toBeUpdated = new LinkedList();
- String myTheme = this.getTheme();
- for (Iterator i = getWindows().iterator(); i.hasNext();) {
- Window w = (Window) i.next();
- String windowTheme = w.getTheme();
- if ((windowTheme == null)
- || (!theme.equals(windowTheme) && windowTheme.equals(myTheme))) {
- toBeUpdated.add(w);
- }
- }
-
- // Update theme
- this.theme = theme;
-
- // Ask windows to update themselves
- for (Iterator i = getWindows().iterator(); i.hasNext();)
- ((Window) i.next()).requestRepaint();
- }
-
- /** Returns the mainWindow of the application.
- * @return Window
- */
- public Window getMainWindow() {
- return mainWindow;
- }
-
- /** Sets the mainWindow. If the main window is not explicitly set, the
- * main window defaults to first created window. Setting window as
- * a main window of this application also adds the window to this application.
- * @param mainWindow The mainWindow to set
- */
- public void setMainWindow(Window mainWindow) {
-
- addWindow(mainWindow);
- this.mainWindow = mainWindow;
- }
-
- /** Returns an enumeration of all the names in this application.
- * @return an enumeration of all the keys in this property list, including the keys in
- * the default property list.
- *
- */
- public Enumeration getPropertyNames() {
- return this.properties.propertyNames();
- }
-
- /** Searches for the property with the specified name in this application.
- * The method returns null if the property is not found.
- *
- * @param name The name of the property.
- * @return The value in this property list with the specified key value.
- */
- public String getProperty(String name) {
- return this.properties.getProperty(name);
- }
-
- /** Add new resource to the application. The resource can be
- * accessed by the user of the application. */
- public void addResource(ApplicationResource resource) {
-
- // Check if the resource is already mapped
- if (resourceKeyMap.containsKey(resource))
- return;
-
- // Generate key
- String key = String.valueOf(++lastResourceKeyNumber);
-
- // Add the resource to mappings
- resourceKeyMap.put(resource, key);
- keyResourceMap.put(key, resource);
- }
-
- /** Remove resource from the application. */
- public void removeResource(ApplicationResource resource) {
- Object key = resourceKeyMap.get(resource);
- if (key != null) {
- resourceKeyMap.remove(resource);
- keyResourceMap.remove(key);
- }
- }
-
- /** Get relative uri of the resource */
- public String getRelativeLocation(ApplicationResource resource) {
-
- // Get the key
- String key = (String) resourceKeyMap.get(resource);
-
- // If the resource is not registered, return null
- if (key == null)
- return null;
-
- String filename = resource.getFilename();
- if (filename == null)
- return "APP/" + key + "/";
- else
- return "APP/" + key + "/" + filename;
- }
-
- /* @see com.enably.tk.terminal.URIHandler#handleURI(URL, String)
- */
- public DownloadStream handleURI(
- URL context,
- String relativeUri) { // If the relative uri is null, we are ready
- if (relativeUri == null)
- return null;
-
- // Resolve prefix
- String prefix = relativeUri;
- int index = relativeUri.indexOf('/');
- if (index >= 0)
- prefix = relativeUri.substring(0, index);
-
- // Handle resource requests
- if (prefix.equals("APP")) {
-
- // Handle resource request
- int next = relativeUri.indexOf('/', index + 1);
- if (next < 0)
- return null;
- String key = relativeUri.substring(index + 1, next);
- ApplicationResource resource =
- (ApplicationResource) keyResourceMap.get(key);
- if (resource != null)
- return resource.getStream();
-
- // Resource requests override uri handling
- return null;
- }
-
- // If the uri is in some window, handle the window uri
- Window window = getWindow(prefix);
- if (window != null) {
- URL windowContext;
- try {
- windowContext = new URL(context, prefix + "/");
- String windowUri =
- relativeUri.length() > prefix.length() + 1
- ? relativeUri.substring(prefix.length() + 1)
- : "";
- return window.handleURI(windowContext, windowUri);
- } catch (MalformedURLException e) {
- return null;
- }
- }
-
- // If the uri was not pointing to a window, handle the
- // uri in main window
- window = getMainWindow();
- if (window != null)
- return window.handleURI(context, relativeUri);
-
- return null;
- }
-
- /** Get thed default locale for this application */
- public Locale getLocale() {
- if (this.locale != null)
- return this.locale;
- return Locale.getDefault();
- }
-
- /** Set the default locale for this application */
- public void setLocale(Locale locale) {
- this.locale = locale;
- }
-
- /** Application user change event sent when the setUser is called to
- * change the current user of the application.
- * @version @VERSION@
- * @since 3.0
- */
- public class UserChangeEvent extends java.util.EventObject {
-
- /**
- * Serial generated by eclipse.
- */
- private static final long serialVersionUID = 3544951069307188281L;
-
- /** New user of the application */
- private Object newUser;
-
- /** Previous user of the application */
- private Object prevUser;
-
- /** Contructor for user change event */
- public UserChangeEvent(
- Application source,
- Object newUser,
- Object prevUser) {
- super(source);
- this.newUser = newUser;
- this.prevUser = prevUser;
- }
-
- /** Get the new user of the application */
- public Object getNewUser() {
- return newUser;
- }
-
- /** Get the previous user of the application */
- public Object getPreviousUser() {
- return prevUser;
- }
-
- /** Get the application where the user change occurred */
- public Application getApplication() {
- return (Application) getSource();
- }
- }
-
- /** Public interface for listening application user changes
- * @version @VERSION@
- * @since 3.0
- */
- public interface UserChangeListener extends EventListener {
-
- /** Invoked when the application user has changed */
- public void applicationUserChanged(Application.UserChangeEvent event);
- }
-
- /** Add user change listener */
- public void addListener(UserChangeListener listener) {
- if (userChangeListeners == null)
- userChangeListeners = new LinkedList();
- userChangeListeners.add(listener);
- }
-
- /** Remove user change listener */
- public void removeListener(UserChangeListener listener) {
- if (userChangeListeners == null)
- return;
- userChangeListeners.remove(listener);
- if (userChangeListeners.isEmpty())
- userChangeListeners = null;
- }
-
- /** Window detach event */
- public class WindowDetachEvent extends EventObject {
-
- /**
- * Serial generated by eclipse.
- */
- private static final long serialVersionUID = 3544669568644691769L;
- private Window window;
-
- /** Create event.
- * @param window Detached window.
- */
- public WindowDetachEvent(Window window) {
- super(Application.this);
- this.window = window;
- }
-
- /** Get the detached window */
- public Window getWindow() {
- return window;
- }
-
- /** Get the application from which the window was detached */
- public Application getApplication() {
- return (Application) getSource();
- }
- }
-
- /** Window attach event */
- public class WindowAttachEvent extends EventObject {
-
- /**
- * Serial generated by eclipse.
- */
- private static final long serialVersionUID = 3977578104367822392L;
- private Window window;
-
- /** Create event.
- * @param window Attached window.
- */
- public WindowAttachEvent(Window window) {
- super(Application.this);
- this.window = window;
- }
-
- /** Get the attached window */
- public Window getWindow() {
- return window;
- }
-
- /** Get the application to which the window was attached */
- public Application getApplication() {
- return (Application) getSource();
- }
- }
-
- /** Window attach listener interface */
- public interface WindowAttachListener {
-
- /** Window attached */
- public void windowAttached(WindowAttachEvent event);
- }
-
- /** Window detach listener interface */
- public interface WindowDetachListener {
-
- /** Window attached */
- public void windowDetached(WindowDetachEvent event);
- }
-
- /** Add window attach listener */
- public void addListener(WindowAttachListener listener) {
- if (windowAttachListeners == null)
- windowAttachListeners = new LinkedList();
- windowAttachListeners.add(listener);
- }
-
- /** Add window detach listener */
- public void addListener(WindowDetachListener listener) {
- if (windowDetachListeners == null)
- windowDetachListeners = new LinkedList();
- windowDetachListeners.add(listener);
- }
-
- /** Remove window attach listener */
- public void removeListener(WindowAttachListener listener) {
- if (windowAttachListeners != null) {
- windowAttachListeners.remove(listener);
- if (windowAttachListeners.isEmpty())
- windowAttachListeners = null;
- }
- }
-
- /** Remove window detach listener */
- public void removeListener(WindowDetachListener listener) {
- if (windowDetachListeners != null) {
- windowDetachListeners.remove(listener);
- if (windowDetachListeners.isEmpty())
- windowDetachListeners = null;
- }
- }
-
- /** Returns the URL user is redirected to on application close.
- * If the URL is null, the application is closed normally as
- * defined by the application running environment: Desctop application
- * just closes the application window and web-application redirects
- * the browser to application main URL.
- *
- * @return URL
- */
- public String getLogoutURL() {
- return logoutURL;
- }
-
- /** Sets the URL user is redirected to on application close.
- * If the URL is null, the application is closed normally as
- * defined by the application running environment: Desctop application
- * just closes the application window and web-application redirects
- * the browser to application main URL.
- *
- * @param logoutURL The logoutURL to set
- */
- public void setLogoutURL(String logoutURL) {
- this.logoutURL = logoutURL;
- }
-
- /**
- * @see com.enably.tk.terminal.Terminal.ErrorListener#terminalError(com.enably.tk.terminal.Terminal.ErrorEvent)
- */
- public void terminalError(Terminal.ErrorEvent event) {
-
- // Find the original source of the error/exception
- Object owner = null;
- if (event instanceof VariableOwner.ErrorEvent) {
- owner = ((VariableOwner.ErrorEvent) event).getVariableOwner();
- } else if (event instanceof URIHandler.ErrorEvent) {
- owner = ((URIHandler.ErrorEvent) event).getURIHandler();
- } else if (event instanceof ParameterHandler.ErrorEvent) {
- owner = ((ParameterHandler.ErrorEvent) event).getParameterHandler();
- }
-
- // Show the error in AbstractComponent
- if (owner instanceof AbstractComponent) {
- Throwable e = event.getThrowable();
- if (e instanceof ErrorMessage)
- ((AbstractComponent) owner).setComponentError((ErrorMessage) e);
- else
- ((AbstractComponent) owner).setComponentError(
- new SystemError(e));
- }
- }
-
- /** Get application context.
- *
- * The application context is the environment where the application
- * is running in.
- */
- public ApplicationContext getContext() {
- return context;
- }
-
- }
|