You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Application.java 32KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.itmill.toolkit;
  5. import java.net.MalformedURLException;
  6. import java.net.URL;
  7. import java.util.Collection;
  8. import java.util.Collections;
  9. import java.util.Enumeration;
  10. import java.util.EventListener;
  11. import java.util.EventObject;
  12. import java.util.Hashtable;
  13. import java.util.Iterator;
  14. import java.util.LinkedList;
  15. import java.util.Locale;
  16. import java.util.Properties;
  17. import java.util.Random;
  18. import com.itmill.toolkit.service.ApplicationContext;
  19. import com.itmill.toolkit.terminal.ApplicationResource;
  20. import com.itmill.toolkit.terminal.DownloadStream;
  21. import com.itmill.toolkit.terminal.ErrorMessage;
  22. import com.itmill.toolkit.terminal.ParameterHandler;
  23. import com.itmill.toolkit.terminal.SystemError;
  24. import com.itmill.toolkit.terminal.Terminal;
  25. import com.itmill.toolkit.terminal.URIHandler;
  26. import com.itmill.toolkit.terminal.VariableOwner;
  27. import com.itmill.toolkit.ui.AbstractComponent;
  28. import com.itmill.toolkit.ui.Component;
  29. import com.itmill.toolkit.ui.Window;
  30. import com.itmill.toolkit.ui.Component.Focusable;
  31. /**
  32. * <p>
  33. * Base class required for all IT Mill Toolkit applications. This class provides
  34. * all the basic services required by the toolkit. These services allow external
  35. * discovery and manipulation of the user,
  36. * {@link com.itmill.toolkit.ui.Window windows} and themes, and starting and
  37. * stopping the application.
  38. * </p>
  39. *
  40. * <p>
  41. * As mentioned, all IT Mill Toolkit applications must inherit this class.
  42. * However, this is almost all of what one needs to do to create a fully
  43. * functional application. The only thing a class inheriting the
  44. * <code>Application</code> needs to do is implement the <code>init</code>
  45. * method where it creates the windows it needs to perform its function. Note
  46. * that all applications must have at least one window: the main window. The
  47. * first unnamed window constructed by an application automatically becomes the
  48. * main window which behaves just like other windows with one exception: when
  49. * accessing windows using URLs the main window corresponds to the application
  50. * URL whereas other windows correspond to a URL gotten by catenating the
  51. * window's name to the application URL.
  52. * </p>
  53. *
  54. * <p>
  55. * See the class <code>com.itmill.toolkit.demo.HelloWorld</code> for a simple
  56. * example of a fully working application.
  57. * </p>
  58. *
  59. * <p>
  60. * <strong>Window access.</strong> <code>Application</code> provides methods
  61. * to list, add and remove the windows it contains.
  62. * </p>
  63. *
  64. * <p>
  65. * <strong>Execution control.</strong> This class includes method to start and
  66. * finish the execution of the application. Being finished means basically that
  67. * no windows will be available from the application anymore.
  68. * </p>
  69. *
  70. * <p>
  71. * <strong>Theme selection.</strong> The theme selection process allows a theme
  72. * to be specified at three different levels. When a window's theme needs to be
  73. * found out, the window itself is queried for a preferred theme. If the window
  74. * does not prefer a specific theme, the application containing the window is
  75. * queried. If neither the application prefers a theme, the default theme for
  76. * the {@link com.itmill.toolkit.terminal.Terminal terminal} is used. The
  77. * terminal always defines a default theme.
  78. * </p>
  79. *
  80. * @author IT Mill Ltd.
  81. * @version
  82. * @VERSION@
  83. * @since 3.0
  84. */
  85. public abstract class Application implements URIHandler, Terminal.ErrorListener {
  86. /**
  87. * Random window name generator.
  88. */
  89. private static Random nameGenerator = new Random();
  90. /**
  91. * Application context the application is running in.
  92. */
  93. private ApplicationContext context;
  94. /**
  95. * The current user or <code>null</code> if no user has logged in.
  96. */
  97. private Object user;
  98. /**
  99. * Mapping from window name to window instance.
  100. */
  101. private final Hashtable windows = new Hashtable();
  102. /**
  103. * Main window of the application.
  104. */
  105. private Window mainWindow = null;
  106. /**
  107. * The application's URL.
  108. */
  109. private URL applicationUrl;
  110. /**
  111. * Name of the theme currently used by the application.
  112. */
  113. private String theme = null;
  114. /**
  115. * Application status.
  116. */
  117. private boolean applicationIsRunning = false;
  118. /**
  119. * Application properties.
  120. */
  121. private Properties properties;
  122. /**
  123. * Default locale of the application.
  124. */
  125. private Locale locale;
  126. /**
  127. * List of listeners listening user changes.
  128. */
  129. private LinkedList userChangeListeners = null;
  130. /**
  131. * Window attach listeners.
  132. */
  133. private LinkedList windowAttachListeners = null;
  134. /**
  135. * Window detach listeners.
  136. */
  137. private LinkedList windowDetachListeners = null;
  138. /**
  139. * Application resource mapping: key <-> resource.
  140. */
  141. private final Hashtable resourceKeyMap = new Hashtable();
  142. private final Hashtable keyResourceMap = new Hashtable();
  143. private long lastResourceKeyNumber = 0;
  144. /**
  145. * URL the user is redirected to on application close or null if application
  146. * is just closed
  147. */
  148. private String logoutURL = null;
  149. private Focusable pendingFocus;
  150. /**
  151. * <p>
  152. * Gets a window by name. Returns <code>null</code> if the application is
  153. * not running or it does not contain a window corresponding to the name.
  154. * </p>
  155. *
  156. * <p>
  157. * Since version 5.0 all windows can be referenced by their names in url
  158. * <code>http://host:port/foo/bar/</code> where
  159. * <code>http://host:port/foo/</code> is the application url as returned
  160. * by getURL() and <code>bar</code> is the name of the window.
  161. * </p>
  162. *
  163. * <p>
  164. * One should note that this method can, as a side effect create new windows
  165. * if needed by the application. This can be achieved by overriding the
  166. * default implementation.
  167. * </p>
  168. *
  169. * <p>
  170. * The method should return null if the window does not exists (and is not
  171. * created as a side-effect) or if the application is not running anymore
  172. * </p>.
  173. *
  174. * @param name
  175. * the name of the window.
  176. * @return the window associated with the given URI or <code>null</code>
  177. */
  178. public Window getWindow(String name) {
  179. // For closed app, do not give any windows
  180. if (!isRunning()) {
  181. return null;
  182. }
  183. // Gets the window by name
  184. final Window window = (Window) windows.get(name);
  185. return window;
  186. }
  187. /**
  188. * Adds a new window to the application.
  189. *
  190. * <p>
  191. * This implicitly invokes the
  192. * {@link com.itmill.toolkit.ui.Window#setApplication(Application)} method.
  193. * </p>
  194. *
  195. * <p>
  196. * Note that all application-level windows can be accessed by their names in
  197. * url <code>http://host:port/foo/bar/</code> where
  198. * <code>http://host:port/foo/</code> is the application url as returned
  199. * by getURL() and <code>bar</code> is the name of the window. Also note
  200. * that not all windows should be added to application - one can also add
  201. * windows inside other windows - these windows show as smaller windows
  202. * inside those windows.
  203. * </p>
  204. *
  205. * @param window
  206. * the new <code>Window</code> to add. If the name of the
  207. * window is <code>null</code>, an unique name is
  208. * automatically given for the window.
  209. * @throws IllegalArgumentException
  210. * if a window with the same name as the new window already
  211. * exists in the application.
  212. * @throws NullPointerException
  213. * if the given <code>Window</code> is <code>null</code>.
  214. */
  215. public void addWindow(Window window) throws IllegalArgumentException,
  216. NullPointerException {
  217. // Nulls can not be added to application
  218. if (window == null) {
  219. return;
  220. }
  221. // Gets the naming proposal from window
  222. String name = window.getName();
  223. // Checks that the application does not already contain
  224. // window having the same name
  225. if (name != null && windows.containsKey(name)) {
  226. // If the window is already added
  227. if (window == windows.get(name)) {
  228. return;
  229. }
  230. // Otherwise complain
  231. throw new IllegalArgumentException("Window with name '"
  232. + window.getName()
  233. + "' is already present in the application");
  234. }
  235. // If the name of the window is null, the window is automatically named
  236. if (name == null) {
  237. boolean accepted = false;
  238. while (!accepted) {
  239. // Try another name
  240. name = String.valueOf(Math.abs(nameGenerator.nextInt()));
  241. if (!windows.containsKey(name)) {
  242. accepted = true;
  243. }
  244. }
  245. window.setName(name);
  246. }
  247. // Adds the window to application
  248. windows.put(name, window);
  249. window.setApplication(this);
  250. fireWindowAttachEvent(window);
  251. // If no main window is set, declare the window to be main window
  252. if (getMainWindow() == null) {
  253. setMainWindow(window);
  254. }
  255. }
  256. /**
  257. * Send information to all listeners about new Windows associated with this
  258. * application.
  259. *
  260. * @param window
  261. */
  262. private void fireWindowAttachEvent(Window window) {
  263. // Fires the window attach event
  264. if (windowAttachListeners != null) {
  265. final Object[] listeners = windowAttachListeners.toArray();
  266. final WindowAttachEvent event = new WindowAttachEvent(window);
  267. for (int i = 0; i < listeners.length; i++) {
  268. ((WindowAttachListener) listeners[i]).windowAttached(event);
  269. }
  270. }
  271. }
  272. /**
  273. * Removes the specified window from the application.
  274. *
  275. * @param window
  276. * the window to be removed.
  277. */
  278. public void removeWindow(Window window) {
  279. if (window != null && windows.contains(window)) {
  280. // Removes the window from application
  281. windows.remove(window.getName());
  282. // If the window was main window, clear it
  283. if (getMainWindow() == window) {
  284. setMainWindow(null);
  285. }
  286. // Removes the application from window
  287. if (window.getApplication() == this) {
  288. window.setApplication(null);
  289. }
  290. fireWindowDetachEvent(window);
  291. }
  292. }
  293. private void fireWindowDetachEvent(Window window) {
  294. // Fires the window detach event
  295. if (windowDetachListeners != null) {
  296. final Object[] listeners = windowDetachListeners.toArray();
  297. final WindowDetachEvent event = new WindowDetachEvent(window);
  298. for (int i = 0; i < listeners.length; i++) {
  299. ((WindowDetachListener) listeners[i]).windowDetached(event);
  300. }
  301. }
  302. }
  303. /**
  304. * Gets the user of the application.
  305. *
  306. * @return the User of the application.
  307. */
  308. public Object getUser() {
  309. return user;
  310. }
  311. /**
  312. * <p>
  313. * Sets the user of the application instance. An application instance may
  314. * have a user associated to it. This can be set in login procedure or
  315. * application initialization.
  316. * </p>
  317. * <p>
  318. * A component performing the user login procedure can assign the user
  319. * property of the application and make the user object available to other
  320. * components of the application.
  321. * </p>
  322. *
  323. * @param user
  324. * the new user.
  325. */
  326. public void setUser(Object user) {
  327. final Object prevUser = this.user;
  328. if (user != prevUser && (user == null || !user.equals(prevUser))) {
  329. this.user = user;
  330. if (userChangeListeners != null) {
  331. final Object[] listeners = userChangeListeners.toArray();
  332. final UserChangeEvent event = new UserChangeEvent(this, user,
  333. prevUser);
  334. for (int i = 0; i < listeners.length; i++) {
  335. ((UserChangeListener) listeners[i])
  336. .applicationUserChanged(event);
  337. }
  338. }
  339. }
  340. }
  341. /**
  342. * Gets the URL of the application.
  343. *
  344. * @return the application's URL.
  345. */
  346. public URL getURL() {
  347. return applicationUrl;
  348. }
  349. /**
  350. * Ends the Application. In effect this will cause the application stop
  351. * returning any windows when asked.
  352. */
  353. public void close() {
  354. applicationIsRunning = false;
  355. }
  356. /**
  357. * Starts the application on the given URL.After this call the application
  358. * corresponds to the given URL and it will return windows when asked for
  359. * them.
  360. *
  361. * @param applicationUrl
  362. * the URL the application should respond to.
  363. * @param applicationProperties
  364. * the Application properties as specified by the adapter.
  365. * @param context
  366. * the context application will be running in.
  367. *
  368. */
  369. public void start(URL applicationUrl, Properties applicationProperties,
  370. ApplicationContext context) {
  371. this.applicationUrl = applicationUrl;
  372. properties = applicationProperties;
  373. this.context = context;
  374. init();
  375. applicationIsRunning = true;
  376. }
  377. /**
  378. * Tests if the application is running or if it has been finished.
  379. *
  380. * @return <code>true</code> if the application is running,
  381. * <code>false</code> if not.
  382. */
  383. public boolean isRunning() {
  384. return applicationIsRunning;
  385. }
  386. /**
  387. * Gets the set of windows contained by the application.
  388. *
  389. * @return the Unmodifiable collection of windows.
  390. */
  391. public Collection getWindows() {
  392. return Collections.unmodifiableCollection(windows.values());
  393. }
  394. /**
  395. * <p>
  396. * Main initializer of the application. The <code>init</code> method is
  397. * called by the framework when the application is started, and it should
  398. * perform whatever initialization operations the application needs, such as
  399. * creating windows and adding components to them.
  400. * </p>
  401. */
  402. public abstract void init();
  403. /**
  404. * Gets the application's theme. The application's theme is the default
  405. * theme used by all the windows in it that do not explicitly specify a
  406. * theme. If the application theme is not explicitly set, the
  407. * <code>null</code> is returned.
  408. *
  409. * @return the name of the application's theme.
  410. */
  411. public String getTheme() {
  412. return theme;
  413. }
  414. /**
  415. * Sets the application's theme.
  416. * <p>
  417. * Note that this theme can be overridden by the windows. <code>null</code>
  418. * implies the default terminal theme.
  419. * </p>
  420. *
  421. * @param theme
  422. * the new theme for this application.
  423. */
  424. public void setTheme(String theme) {
  425. // Collect list of windows not having the current or future theme
  426. final LinkedList toBeUpdated = new LinkedList();
  427. final String myTheme = getTheme();
  428. for (final Iterator i = getWindows().iterator(); i.hasNext();) {
  429. final Window w = (Window) i.next();
  430. final String windowTheme = w.getTheme();
  431. if ((windowTheme == null)
  432. || (!theme.equals(windowTheme) && windowTheme
  433. .equals(myTheme))) {
  434. toBeUpdated.add(w);
  435. }
  436. }
  437. // Updates the theme
  438. this.theme = theme;
  439. // Ask windows to update themselves
  440. for (final Iterator i = toBeUpdated.iterator(); i.hasNext();) {
  441. ((Window) i.next()).requestRepaint();
  442. }
  443. }
  444. /**
  445. * Gets the mainWindow of the application.
  446. *
  447. * @return the main window.
  448. */
  449. public Window getMainWindow() {
  450. return mainWindow;
  451. }
  452. /**
  453. * <p>
  454. * Sets the mainWindow. If the main window is not explicitly set, the main
  455. * window defaults to first created window. Setting window as a main window
  456. * of this application also adds the window to this application.
  457. * </p>
  458. *
  459. * @param mainWindow
  460. * the mainWindow to set.
  461. */
  462. public void setMainWindow(Window mainWindow) {
  463. addWindow(mainWindow);
  464. this.mainWindow = mainWindow;
  465. }
  466. /**
  467. * Returns an enumeration of all the names in this application.
  468. *
  469. * @return an enumeration of all the keys in this property list, including
  470. * the keys in the default property list.
  471. *
  472. */
  473. public Enumeration getPropertyNames() {
  474. return properties.propertyNames();
  475. }
  476. /**
  477. * Searches for the property with the specified name in this application.
  478. * This method returns <code>null</code> if the property is not found.
  479. *
  480. * @param name
  481. * the name of the property.
  482. * @return the value in this property list with the specified key value.
  483. */
  484. public String getProperty(String name) {
  485. return properties.getProperty(name);
  486. }
  487. /**
  488. * Adds new resource to the application. The resource can be accessed by the
  489. * user of the application.
  490. *
  491. * @param resource
  492. * the resource to add.
  493. */
  494. public void addResource(ApplicationResource resource) {
  495. // Check if the resource is already mapped
  496. if (resourceKeyMap.containsKey(resource)) {
  497. return;
  498. }
  499. // Generate key
  500. final String key = String.valueOf(++lastResourceKeyNumber);
  501. // Add the resource to mappings
  502. resourceKeyMap.put(resource, key);
  503. keyResourceMap.put(key, resource);
  504. }
  505. /**
  506. * Removes the resource from the application.
  507. *
  508. * @param resource
  509. * the resource to remove.
  510. */
  511. public void removeResource(ApplicationResource resource) {
  512. final Object key = resourceKeyMap.get(resource);
  513. if (key != null) {
  514. resourceKeyMap.remove(resource);
  515. keyResourceMap.remove(key);
  516. }
  517. }
  518. /**
  519. * Gets the relative uri of the resource.
  520. *
  521. * @param resource
  522. * the resource to get relative location.
  523. * @return the relative uri of the resource.
  524. */
  525. public String getRelativeLocation(ApplicationResource resource) {
  526. // Gets the key
  527. final String key = (String) resourceKeyMap.get(resource);
  528. // If the resource is not registered, return null
  529. if (key == null) {
  530. return null;
  531. }
  532. final String filename = resource.getFilename();
  533. if (filename == null) {
  534. return "APP/" + key + "/";
  535. } else {
  536. return "APP/" + key + "/" + filename;
  537. }
  538. }
  539. /*
  540. * @see com.itmill.toolkit.terminal.URIHandler#handleURI(URL, String)
  541. */
  542. public DownloadStream handleURI(URL context, String relativeUri) {
  543. // If the relative uri is null, we are ready
  544. if (relativeUri == null) {
  545. return null;
  546. }
  547. // Resolves the prefix
  548. String prefix = relativeUri;
  549. final int index = relativeUri.indexOf('/');
  550. if (index >= 0) {
  551. prefix = relativeUri.substring(0, index);
  552. }
  553. // Handles the resource requests
  554. if (prefix.equals("APP")) {
  555. // Handles the resource request
  556. final int next = relativeUri.indexOf('/', index + 1);
  557. if (next < 0) {
  558. return null;
  559. }
  560. final String key = relativeUri.substring(index + 1, next);
  561. final ApplicationResource resource = (ApplicationResource) keyResourceMap
  562. .get(key);
  563. if (resource != null) {
  564. return resource.getStream();
  565. }
  566. // Resource requests override uri handling
  567. return null;
  568. }
  569. // If the uri is in some window, handle the window uri
  570. Window window = getWindow(prefix);
  571. if (window != null) {
  572. URL windowContext;
  573. try {
  574. windowContext = new URL(context, prefix + "/");
  575. final String windowUri = relativeUri.length() > prefix.length() + 1 ? relativeUri
  576. .substring(prefix.length() + 1)
  577. : "";
  578. return window.handleURI(windowContext, windowUri);
  579. } catch (final MalformedURLException e) {
  580. return null;
  581. }
  582. }
  583. // If the uri was not pointing to a window, handle the
  584. // uri in main window
  585. window = getMainWindow();
  586. if (window != null) {
  587. return window.handleURI(context, relativeUri);
  588. }
  589. return null;
  590. }
  591. /**
  592. * Gets the default locale for this application.
  593. *
  594. * @return the locale of this application.
  595. */
  596. public Locale getLocale() {
  597. if (locale != null) {
  598. return locale;
  599. }
  600. return Locale.getDefault();
  601. }
  602. /**
  603. * Sets the default locale for this application.
  604. *
  605. * @param locale
  606. * the Locale object.
  607. *
  608. */
  609. public void setLocale(Locale locale) {
  610. this.locale = locale;
  611. }
  612. /**
  613. * <p>
  614. * An event that characterizes a change in the current selection.
  615. * </p>
  616. * Application user change event sent when the setUser is called to change
  617. * the current user of the application.
  618. *
  619. * @version
  620. * @VERSION@
  621. * @since 3.0
  622. */
  623. public class UserChangeEvent extends java.util.EventObject {
  624. /**
  625. * Serial generated by eclipse.
  626. */
  627. private static final long serialVersionUID = 3544951069307188281L;
  628. /**
  629. * New user of the application.
  630. */
  631. private final Object newUser;
  632. /**
  633. * Previous user of the application.
  634. */
  635. private final Object prevUser;
  636. /**
  637. * Constructor for user change event.
  638. *
  639. * @param source
  640. * the application source.
  641. * @param newUser
  642. * the new User.
  643. * @param prevUser
  644. * the previous User.
  645. */
  646. public UserChangeEvent(Application source, Object newUser,
  647. Object prevUser) {
  648. super(source);
  649. this.newUser = newUser;
  650. this.prevUser = prevUser;
  651. }
  652. /**
  653. * Gets the new user of the application.
  654. *
  655. * @return the new User.
  656. */
  657. public Object getNewUser() {
  658. return newUser;
  659. }
  660. /**
  661. * Gets the previous user of the application.
  662. *
  663. * @return the previous Toolkit user, if user has not changed ever on
  664. * application it returns <code>null</code>
  665. */
  666. public Object getPreviousUser() {
  667. return prevUser;
  668. }
  669. /**
  670. * Gets the application where the user change occurred.
  671. *
  672. * @return the Application.
  673. */
  674. public Application getApplication() {
  675. return (Application) getSource();
  676. }
  677. }
  678. /**
  679. * The <code>UserChangeListener</code> interface for listening application
  680. * user changes.
  681. *
  682. * @version
  683. * @VERSION@
  684. * @since 3.0
  685. */
  686. public interface UserChangeListener extends EventListener {
  687. /**
  688. * The <code>applicationUserChanged</code> method Invoked when the
  689. * application user has changed.
  690. *
  691. * @param event
  692. * the change event.
  693. */
  694. public void applicationUserChanged(Application.UserChangeEvent event);
  695. }
  696. /**
  697. * Adds the user change listener.
  698. *
  699. * @param listener
  700. * the user change listener to add.
  701. */
  702. public void addListener(UserChangeListener listener) {
  703. if (userChangeListeners == null) {
  704. userChangeListeners = new LinkedList();
  705. }
  706. userChangeListeners.add(listener);
  707. }
  708. /**
  709. * Removes the user change listener.
  710. *
  711. * @param listener
  712. * the user change listener to remove.
  713. */
  714. public void removeListener(UserChangeListener listener) {
  715. if (userChangeListeners == null) {
  716. return;
  717. }
  718. userChangeListeners.remove(listener);
  719. if (userChangeListeners.isEmpty()) {
  720. userChangeListeners = null;
  721. }
  722. }
  723. /**
  724. * Window detach event.
  725. */
  726. public class WindowDetachEvent extends EventObject {
  727. /**
  728. * Serial generated by eclipse.
  729. */
  730. private static final long serialVersionUID = 3544669568644691769L;
  731. private final Window window;
  732. /**
  733. * Creates a event.
  734. *
  735. * @param window
  736. * the Detached window.
  737. */
  738. public WindowDetachEvent(Window window) {
  739. super(Application.this);
  740. this.window = window;
  741. }
  742. /**
  743. * Gets the detached window.
  744. *
  745. * @return the detached window.
  746. */
  747. public Window getWindow() {
  748. return window;
  749. }
  750. /**
  751. * Gets the application from which the window was detached.
  752. *
  753. * @return the Application.
  754. */
  755. public Application getApplication() {
  756. return (Application) getSource();
  757. }
  758. }
  759. /**
  760. * Window attach event.
  761. */
  762. public class WindowAttachEvent extends EventObject {
  763. /**
  764. * Serial generated by eclipse.
  765. */
  766. private static final long serialVersionUID = 3977578104367822392L;
  767. private final Window window;
  768. /**
  769. * Creates a event.
  770. *
  771. * @param window
  772. * the Attached window.
  773. */
  774. public WindowAttachEvent(Window window) {
  775. super(Application.this);
  776. this.window = window;
  777. }
  778. /**
  779. * Gets the attached window.
  780. *
  781. * @return the attached window.
  782. */
  783. public Window getWindow() {
  784. return window;
  785. }
  786. /**
  787. * Gets the application to which the window was attached.
  788. *
  789. * @return the Application.
  790. */
  791. public Application getApplication() {
  792. return (Application) getSource();
  793. }
  794. }
  795. /**
  796. * Window attach listener interface.
  797. */
  798. public interface WindowAttachListener {
  799. /**
  800. * Window attached
  801. *
  802. * @param event
  803. * the window attach event.
  804. */
  805. public void windowAttached(WindowAttachEvent event);
  806. }
  807. /**
  808. * Window detach listener interface.
  809. */
  810. public interface WindowDetachListener {
  811. /**
  812. * Window detached.
  813. *
  814. * @param event
  815. * the window detach event.
  816. */
  817. public void windowDetached(WindowDetachEvent event);
  818. }
  819. /**
  820. * Adds the window attach listener.
  821. *
  822. * @param listener
  823. * the window attach listener to add.
  824. */
  825. public void addListener(WindowAttachListener listener) {
  826. if (windowAttachListeners == null) {
  827. windowAttachListeners = new LinkedList();
  828. }
  829. windowAttachListeners.add(listener);
  830. }
  831. /**
  832. * Adds the window detach listener.
  833. *
  834. * @param listener
  835. * the window detach listener to add.
  836. */
  837. public void addListener(WindowDetachListener listener) {
  838. if (windowDetachListeners == null) {
  839. windowDetachListeners = new LinkedList();
  840. }
  841. windowDetachListeners.add(listener);
  842. }
  843. /**
  844. * Removes the window attach listener.
  845. *
  846. * @param listener
  847. * the window attach listener to remove.
  848. */
  849. public void removeListener(WindowAttachListener listener) {
  850. if (windowAttachListeners != null) {
  851. windowAttachListeners.remove(listener);
  852. if (windowAttachListeners.isEmpty()) {
  853. windowAttachListeners = null;
  854. }
  855. }
  856. }
  857. /**
  858. * Removes the window detach listener.
  859. *
  860. * @param listener
  861. * the window detach listener to remove.
  862. */
  863. public void removeListener(WindowDetachListener listener) {
  864. if (windowDetachListeners != null) {
  865. windowDetachListeners.remove(listener);
  866. if (windowDetachListeners.isEmpty()) {
  867. windowDetachListeners = null;
  868. }
  869. }
  870. }
  871. /**
  872. * Returns the URL user is redirected to on application close.If the URL is
  873. * <code>null</code>, the application is closed normally as defined by
  874. * the application running environment.
  875. * <p>
  876. * Desktop application just closes the application window and
  877. * web-application redirects the browser to application main URL.
  878. * </p>
  879. *
  880. * @return the URL.
  881. */
  882. public String getLogoutURL() {
  883. return logoutURL;
  884. }
  885. /**
  886. * Sets the URL user is redirected to on application close. If the URL is
  887. * <code>null</code>, the application is closed normally as defined by
  888. * the application running environment: Desktop application just closes the
  889. * application window and web-application redirects the browser to
  890. * application main URL.
  891. *
  892. * @param logoutURL
  893. * the logoutURL to set.
  894. */
  895. public void setLogoutURL(String logoutURL) {
  896. this.logoutURL = logoutURL;
  897. }
  898. /**
  899. * <p>
  900. * Invoked by the terminal on any exception that occurs in application and
  901. * is thrown by the <code>setVariable</code> to the terminal. The default
  902. * implementation sets the exceptions as <code>ComponentErrors</code> to
  903. * the component that initiated the exception.
  904. * </p>
  905. * <p>
  906. * You can safely override this method in your application in order to
  907. * direct the errors to some other destination (for example log).
  908. * </p>
  909. *
  910. * @param event
  911. * the change event.
  912. * @see com.itmill.toolkit.terminal.Terminal.ErrorListener#terminalError(com.itmill.toolkit.terminal.Terminal.ErrorEvent)
  913. */
  914. public void terminalError(Terminal.ErrorEvent event) {
  915. // Finds the original source of the error/exception
  916. Object owner = null;
  917. if (event instanceof VariableOwner.ErrorEvent) {
  918. owner = ((VariableOwner.ErrorEvent) event).getVariableOwner();
  919. } else if (event instanceof URIHandler.ErrorEvent) {
  920. owner = ((URIHandler.ErrorEvent) event).getURIHandler();
  921. } else if (event instanceof ParameterHandler.ErrorEvent) {
  922. owner = ((ParameterHandler.ErrorEvent) event).getParameterHandler();
  923. }
  924. // Shows the error in AbstractComponent
  925. if (owner instanceof AbstractComponent) {
  926. final Throwable e = event.getThrowable();
  927. if (e instanceof ErrorMessage) {
  928. ((AbstractComponent) owner).setComponentError((ErrorMessage) e);
  929. } else {
  930. ((AbstractComponent) owner)
  931. .setComponentError(new SystemError(e));
  932. }
  933. }
  934. }
  935. /**
  936. * Gets the application context.
  937. * <p>
  938. * The application context is the environment where the application is
  939. * running in.
  940. * </p>
  941. *
  942. * @return the application context.
  943. */
  944. public ApplicationContext getContext() {
  945. return context;
  946. }
  947. public void setFocusedComponent(Focusable focusable) {
  948. pendingFocus = focusable;
  949. }
  950. /**
  951. * Gets and nulls focused component in this window
  952. *
  953. * @return Focused component or null if none is focused.
  954. */
  955. public Component.Focusable consumeFocus() {
  956. final Component.Focusable f = pendingFocus;
  957. pendingFocus = null;
  958. return f;
  959. }
  960. }