diff options
Diffstat (limited to 'src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java')
-rw-r--r-- | src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java | 1053 |
1 files changed, 1053 insertions, 0 deletions
diff --git a/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java b/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java new file mode 100644 index 0000000000..b2bcc68ee5 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java @@ -0,0 +1,1053 @@ +/* ************************************************************************* + + IT Mill Toolkit + + Development of Browser User Interfaces 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.pdf. Use of this product might + require purchasing a commercial license from IT Mill Ltd. For guidelines + on usage, see 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@itmill.com + Finland company www: www.itmill.com + + Primary source for information and releases: www.itmill.com + + ********************************************************************** */ + +package com.itmill.toolkit.terminal.gwt.server; + +import java.io.BufferedWriter; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.text.DateFormatSymbols; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; + +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.itmill.toolkit.Application; +import com.itmill.toolkit.Application.WindowAttachEvent; +import com.itmill.toolkit.Application.WindowDetachEvent; +import com.itmill.toolkit.terminal.DownloadStream; +import com.itmill.toolkit.terminal.PaintTarget; +import com.itmill.toolkit.terminal.Paintable; +import com.itmill.toolkit.terminal.URIHandler; +import com.itmill.toolkit.terminal.VariableOwner; +import com.itmill.toolkit.terminal.Paintable.RepaintRequestEvent; +import com.itmill.toolkit.ui.Component; +import com.itmill.toolkit.ui.FrameWindow; +import com.itmill.toolkit.ui.Window; + +/** + * Application manager processes changes and paints for single application + * instance. + * + * @author IT Mill Ltd. + * @version + * @VERSION@ + * @since 5.0 + */ +public class CommunicationManager implements Paintable.RepaintRequestListener, + Application.WindowAttachListener, Application.WindowDetachListener { + + private static String GET_PARAM_REPAINT_ALL = "repaintAll"; + + private static int DEFAULT_BUFFER_SIZE = 32 * 1024; + + private static int MAX_BUFFER_SIZE = 64 * 1024; + + private WeakHashMap applicationToVariableMapMap = new WeakHashMap(); + + private HashSet dirtyPaintabletSet = new HashSet(); + + private WeakHashMap paintableIdMap = new WeakHashMap(); + + private WeakHashMap idPaintableMap = new WeakHashMap(); + + private int idSequence = 0; + + private Application application; + + private Set removedWindows = new HashSet(); + + private JsonPaintTarget paintTarget; + + private List locales; + + private int pendingLocalesIndex; + + private ApplicationServlet applicationServlet; + + public CommunicationManager(Application application, + ApplicationServlet applicationServlet) { + this.application = application; + this.applicationServlet = applicationServlet; + requireLocale(application.getLocale().toString()); + } + + /** + * + * + */ + public void takeControl() { + application.addListener((Application.WindowAttachListener) this); + application.addListener((Application.WindowDetachListener) this); + + } + + /** + * + * + */ + public void releaseControl() { + application.removeListener((Application.WindowAttachListener) this); + application.removeListener((Application.WindowDetachListener) this); + } + + public void handleUidlRequest(HttpServletRequest request, + HttpServletResponse response) throws IOException { + + // repaint requested or sesssion has timed out and new one is created + boolean repaintAll = (request.getParameter(GET_PARAM_REPAINT_ALL) != null) + || request.getSession().isNew(); + + OutputStream out = response.getOutputStream(); + PrintWriter outWriter = new PrintWriter(new BufferedWriter( + new OutputStreamWriter(out, "UTF-8"))); + + // TODO Move dirt elsewhere + outWriter.print(")/*{"); // some dirt to prevent cross site scripting + // vulnerabilities + + try { + + // Is this a download request from application + DownloadStream download = null; + + // The rest of the process is synchronized with the application + // in order to guarantee that no parallel variable handling is + // made + synchronized (application) { + + // Change all variables based on request parameters + Map unhandledParameters = handleVariables(request, application); + + // Handles the URI if the application is still running + if (application.isRunning()) + download = handleURI(application, request, response); + + // If this is not a download request + if (download == null) { + + // Finds the window within the application + Window window = null; + if (application.isRunning()) + window = getApplicationWindow(request, application); + + // Handles the unhandled parameters if the application is + // still running + if (window != null && unhandledParameters != null + && !unhandledParameters.isEmpty()) + window.handleParameters(unhandledParameters); + + // Removes application if it has stopped + if (!application.isRunning()) { + endApplication(request, response, application); + return; + } + + // Returns if no window found + if (window == null) + return; + + // Sets the response type + response.setContentType("application/json; charset=UTF-8"); + outWriter.print("\"changes\":["); + + paintTarget = new JsonPaintTarget(this, outWriter); + + // Paints components + Set paintables; + if (repaintAll) { + paintables = new LinkedHashSet(); + paintables.add(window); + + // Reset sent locales + locales = null; + requireLocale(application.getLocale().toString()); + + // Adds all non-native windows + for (Iterator i = window.getApplication().getWindows() + .iterator(); i.hasNext();) { + Window w = (Window) i.next(); + if (!"native".equals(w.getStyle()) && w != window) + paintables.add(w); + } + } else + paintables = getDirtyComponents(); + if (paintables != null) { + + // Creates "working copy" of the current state. + List currentPaintables = new ArrayList(paintables); + + // Sorts the paintable so that parent windows + // are always painted before child windows + Collections.sort(currentPaintables, new Comparator() { + + public int compare(Object o1, Object o2) { + + // If first argumement is now window + // the second is "smaller" if it is. + if (!(o1 instanceof Window)) { + return (o2 instanceof Window) ? 1 : 0; + } + + // Now, if second is not window the + // first is smaller. + if (!(o2 instanceof Window)) { + return -1; + } + + // Both are windows. + String n1 = ((Window) o1).getName(); + String n2 = ((Window) o2).getName(); + if (o1 instanceof FrameWindow) { + if (((FrameWindow) o1).getFrameset() + .getFrame(n2) != null) { + return -1; + } else if (!(o2 instanceof FrameWindow)) { + return -1; + } + } + if (o2 instanceof FrameWindow) { + if (((FrameWindow) o2).getFrameset() + .getFrame(n1) != null) { + return 1; + } else if (!(o1 instanceof FrameWindow)) { + return 1; + } + } + + return 0; + } + }); + + for (Iterator i = currentPaintables.iterator(); i + .hasNext();) { + Paintable p = (Paintable) i.next(); + + // TODO CLEAN + if (p instanceof Window) { + Window w = (Window) p; + if (w.getTerminal() == null) + w.setTerminal(application.getMainWindow() + .getTerminal()); + } + + paintTarget.startTag("change"); + paintTarget.addAttribute("format", "uidl"); + String pid = getPaintableId(p); + paintTarget.addAttribute("pid", pid); + + // Track paints to identify empty paints + paintTarget.setTrackPaints(true); + p.paint(paintTarget); + + // If no paints add attribute empty + if (paintTarget.getNumberOfPaints() <= 0) { + paintTarget.addAttribute("visible", false); + } + paintTarget.endTag("change"); + paintablePainted(p); + } + } + + paintTarget.close(); + outWriter.print("]"); // close changes + + outWriter.print(", \"meta\" : {"); + boolean metaOpen = false; + + // .. or initializion (first uidl-request) + if (application.ajaxInit()) { + outWriter.print("\"appInit\":true"); + } + // add meta instruction for client to set focus if it is set + Paintable f = (Paintable) application.consumeFocus(); + if (f != null) { + if (metaOpen) + outWriter.append(","); + outWriter.write("\"focus\":\"" + getPaintableId(f) + + "\""); + } + + outWriter.print("}, \"resources\" : {"); + + // Precache custom layouts + // TODO Does not support theme-get param or different themes + // in different windows -> Allways preload layouts with the + // theme specified by the applications + String themeName = application.getTheme() != null ? application + .getTheme() + : ApplicationServlet.DEFAULT_THEME; + // TODO We should only precache the layouts that are not + // cached already + int resourceIndex = 0; + for (Iterator i = paintTarget.getPreCachedResources() + .iterator(); i.hasNext();) { + String resource = (String) i.next(); + InputStream is = null; + try { + is = applicationServlet + .getServletContext() + .getResourceAsStream( + ApplicationServlet.THEME_DIRECTORY_PATH + + themeName + + "/" + + resource); + } catch (Exception e) { + Log.info(e.getMessage()); + } + if (is != null) { + + outWriter.print((resourceIndex++ > 0 ? ", " : "") + + "\"" + resource + "\" : "); + StringBuffer layout = new StringBuffer(); + + try { + InputStreamReader r = new InputStreamReader(is); + char[] buffer = new char[20000]; + int charsRead = 0; + while ((charsRead = r.read(buffer)) > 0) + layout.append(buffer, 0, charsRead); + r.close(); + } catch (java.io.IOException e) { + Log.info("Resource transfer failed: " + + request.getRequestURI() + ". (" + + e.getMessage() + ")"); + } + outWriter.print("\"" + + JsonPaintTarget.escapeJSON(layout + .toString()) + "\""); + } + } + outWriter.print("}"); + + printLocaleDeclarations(outWriter); + + outWriter.flush(); + outWriter.close(); + out.flush(); + } else { + + // For download request, transfer the downloaded data + handleDownload(download, request, response); + } + } + + out.flush(); + out.close(); + + } catch (Throwable e) { + // Writes the error report to client + OutputStreamWriter w = new OutputStreamWriter(out); + PrintWriter err = new PrintWriter(w); + err + .write("<html><head><title>Application Internal Error</title></head><body>"); + err.write("<h1>" + e.toString() + "</h1><pre>\n"); + e.printStackTrace(new PrintWriter(err)); + err.write("\n</pre></body></html>"); + err.close(); + } finally { + + } + + } + + private Map handleVariables(HttpServletRequest request, + Application application2) { + + Map params = new HashMap(request.getParameterMap()); + String changes = (String) ((params.get("changes") instanceof String[]) ? ((String[]) params + .get("changes"))[0] + : params.get("changes")); + params.remove("changes"); + if (changes != null) { + String[] ca = changes.split("\u0001"); + System.out.println("Changes = " + changes); + for (int i = 0; i < ca.length; i++) { + String[] vid = ca[i].split("_"); + VariableOwner owner = (VariableOwner) idPaintableMap + .get(vid[0]); + if (owner != null) { + Map m; + if (i + 2 >= ca.length + || !vid[0].equals(ca[i + 2].split("_")[0])) + m = new SingleValueMap(vid[1], convertVariableValue( + vid[2].charAt(0), ca[++i])); + else { + m = new HashMap(); + m.put(vid[1], convertVariableValue(vid[2].charAt(0), + ca[++i])); + } + while (i + 1 < ca.length + && vid[0].equals(ca[i + 1].split("_")[0])) { + vid = ca[++i].split("_"); + m.put(vid[1], convertVariableValue(vid[2].charAt(0), + ca[++i])); + } + owner.changeVariables(request, m); + } + } + } + + return params; + } + + private Object convertVariableValue(char variableType, String strValue) { + Object val = null; + System.out.println("converting " + strValue + " of type " + + variableType); + switch (variableType) { + case 'a': + val = strValue.split(","); + break; + case 's': + val = strValue; + break; + case 'i': + val = Integer.valueOf(strValue); + break; + case 'b': + val = Boolean.valueOf(strValue); + break; + } + + System.out.println("result: " + val + " of type " + (val == null ? "-" : val.getClass())); + return val; + } + + private void printLocaleDeclarations(PrintWriter outWriter) { + /* + * ----------------------------- Sending Locale sensitive date + * ----------------------------- + */ + + // Store JVM default locale for later restoration + // (we'll have to change the default locale for a while) + Locale jvmDefault = Locale.getDefault(); + + // Send locale informations to client + outWriter.print(", \"locales\":["); + for (; pendingLocalesIndex < locales.size(); pendingLocalesIndex++) { + + Locale l = generateLocale((String) locales.get(pendingLocalesIndex)); + // Locale name + outWriter.print("{\"name\":\"" + l.toString() + "\","); + + /* + * Month names (both short and full) + */ + DateFormatSymbols dfs = new DateFormatSymbols(l); + String[] short_months = dfs.getShortMonths(); + String[] months = dfs.getMonths(); + outWriter.print("\"smn\":[\"" + + // ShortMonthNames + short_months[0] + "\",\"" + short_months[1] + "\",\"" + + short_months[2] + "\",\"" + short_months[3] + "\",\"" + + short_months[4] + "\",\"" + short_months[5] + "\",\"" + + short_months[6] + "\",\"" + short_months[7] + "\",\"" + + short_months[8] + "\",\"" + short_months[9] + "\",\"" + + short_months[10] + "\",\"" + short_months[11] + "\"" + + "],"); + outWriter.print("\"mn\":[\"" + + // MonthNames + months[0] + "\",\"" + months[1] + "\",\"" + months[2] + + "\",\"" + months[3] + "\",\"" + months[4] + "\",\"" + + months[5] + "\",\"" + months[6] + "\",\"" + months[7] + + "\",\"" + months[8] + "\",\"" + months[9] + "\",\"" + + months[10] + "\",\"" + months[11] + "\"" + "],"); + + /* + * Weekday names (both short and full) + */ + String[] short_days = dfs.getShortWeekdays(); + String[] days = dfs.getWeekdays(); + outWriter.print("\"sdn\":[\"" + + // ShortDayNames + short_days[1] + "\",\"" + short_days[2] + "\",\"" + + short_days[3] + "\",\"" + short_days[4] + "\",\"" + + short_days[5] + "\",\"" + short_days[6] + "\",\"" + + short_days[7] + "\"" + "],"); + outWriter.print("\"dn\":[\"" + + // DayNames + days[1] + "\",\"" + days[2] + "\",\"" + days[3] + "\",\"" + + days[4] + "\",\"" + days[5] + "\",\"" + days[6] + "\",\"" + + days[7] + "\"" + "],"); + + /* + * First day of week (0 = sunday, 1 = monday) + */ + Calendar cal = new GregorianCalendar(l); + outWriter.print("\"fdow\":" + (cal.getFirstDayOfWeek() - 1) + ","); + + /* + * Date formatting (MM/DD/YYYY etc.) + */ + // Force our locale as JVM default for a while (SimpleDateFormat + // uses JVM default) + Locale.setDefault(l); + String df = new SimpleDateFormat().toPattern(); + int timeStart = df.indexOf("H"); + if (timeStart < 0) + timeStart = df.indexOf("h"); + int ampm_first = df.indexOf("a"); + // E.g. in Korean locale AM/PM is before h:mm + // TODO should take that into consideration on client-side as well, + // now always h:mm a + if (ampm_first > 0 && ampm_first < timeStart) + timeStart = ampm_first; + String dateformat = df.substring(0, timeStart - 1); + + outWriter.print("\"df\":\"" + dateformat.trim() + "\","); + + /* + * Time formatting (24 or 12 hour clock and AM/PM suffixes) + */ + String timeformat = df.substring(timeStart, df.length()); // Doesn't + // return + // second + // or + // milliseconds + // We use timeformat to determine 12/24-hour clock + boolean twelve_hour_clock = timeformat.contains("a"); + // TODO there are other possibilities as well, like 'h' in french + // (ignore them, too complicated) + String hour_min_delimiter = timeformat.contains(".") ? "." : ":"; + // outWriter.print("\"tf\":\"" + timeformat + "\","); + outWriter.print("\"thc\":" + twelve_hour_clock + ","); + outWriter.print("\"hmd\":\"" + hour_min_delimiter + "\""); + if (twelve_hour_clock) { + String[] ampm = dfs.getAmPmStrings(); + outWriter.print(",\"ampm\":[\"" + ampm[0] + "\",\"" + ampm[1] + + "\"]"); + } + outWriter.print("}"); + if (pendingLocalesIndex < locales.size() - 1) + outWriter.print(","); + } + outWriter.print("]"); // Close locales + + // Restore JVM default locale + Locale.setDefault(jvmDefault); + } + + /** + * Gets the existing application or create a new one. Get a window within an + * application based on the requested URI. + * + * @param request + * the HTTP Request. + * @param application + * the Application to query for window. + * @return Window mathing the given URI or null if not found. + * @throws ServletException + * if an exception has occurred that interferes with the + * servlet's normal operation. + */ + private Window getApplicationWindow(HttpServletRequest request, + Application application) throws ServletException { + + Window window = null; + + // Find the window where the request is handled + String path = request.getPathInfo(); + + // Main window as the URI is empty + if (path == null || path.length() == 0 || path.equals("/")) + window = application.getMainWindow(); + + // Try to search by window name + else { + String windowName = null; + if (path.charAt(0) == '/') + path = path.substring(1); + int index = path.indexOf('/'); + if (index < 0) { + windowName = path; + path = ""; + } else { + windowName = path.substring(0, index); + path = path.substring(index + 1); + } + window = application.getWindow(windowName); + + // By default, we use main window + if (window == null) + window = application.getMainWindow(); + } + + return window; + } + + /** + * Handles the requested URI. An application can add handlers to do special + * processing, when a certain URI is requested. The handlers are invoked + * before any windows URIs are processed and if a DownloadStream is returned + * it is sent to the client. + * + * @param application + * the Application owning the URI. + * @param request + * the HTTP request instance. + * @param response + * the HTTP response to write to. + * @return boolean <code>true</code> if the request was handled and + * further processing should be suppressed, otherwise + * <code>false</code>. + * @see com.itmill.toolkit.terminal.URIHandler + */ + private DownloadStream handleURI(Application application, + HttpServletRequest request, HttpServletResponse response) { + + String uri = request.getPathInfo(); + + // If no URI is available + if (uri == null || uri.length() == 0 || uri.equals("/")) + return null; + + // Remove the leading / + while (uri.startsWith("/") && uri.length() > 0) + uri = uri.substring(1); + + // Handle the uri + DownloadStream stream = null; + try { + stream = application.handleURI(application.getURL(), uri); + } catch (Throwable t) { + application.terminalError(new URIHandlerErrorImpl(application, t)); + } + + return stream; + } + + /** + * Handles the requested URI. An application can add handlers to do special + * processing, when a certain URI is requested. The handlers are invoked + * before any windows URIs are processed and if a DownloadStream is returned + * it is sent to the client. + * + * @param stream + * the downloadable stream. + * + * @param request + * the HTTP request instance. + * @param response + * the HTTP response to write to. + * + * @see com.itmill.toolkit.terminal.URIHandler + */ + private void handleDownload(DownloadStream stream, + HttpServletRequest request, HttpServletResponse response) { + + // Download from given stream + InputStream data = stream.getStream(); + if (data != null) { + + // Sets content type + response.setContentType(stream.getContentType()); + + // Sets cache headers + long cacheTime = stream.getCacheTime(); + if (cacheTime <= 0) { + response.setHeader("Cache-Control", "no-cache"); + response.setHeader("Pragma", "no-cache"); + response.setDateHeader("Expires", 0); + } else { + response.setHeader("Cache-Control", "max-age=" + cacheTime + / 1000); + response.setDateHeader("Expires", System.currentTimeMillis() + + cacheTime); + response.setHeader("Pragma", "cache"); // Required to apply + // caching in some + // Tomcats + } + + // Copy download stream parameters directly + // to HTTP headers. + Iterator i = stream.getParameterNames(); + if (i != null) { + while (i.hasNext()) { + String param = (String) i.next(); + response.setHeader((String) param, stream + .getParameter(param)); + } + } + + int bufferSize = stream.getBufferSize(); + if (bufferSize <= 0 || bufferSize > MAX_BUFFER_SIZE) + bufferSize = DEFAULT_BUFFER_SIZE; + byte[] buffer = new byte[bufferSize]; + int bytesRead = 0; + + try { + OutputStream out = response.getOutputStream(); + + while ((bytesRead = data.read(buffer)) > 0) { + out.write(buffer, 0, bytesRead); + out.flush(); + } + out.close(); + } catch (IOException ignored) { + } + + } + + } + + /** + * Ends the Application. + * + * @param request + * the HTTP request instance. + * @param response + * the HTTP response to write to. + * @param application + * the Application to end. + * @throws IOException + * if the writing failed due to input/output error. + */ + private void endApplication(HttpServletRequest request, + HttpServletResponse response, Application application) + throws IOException { + + String logoutUrl = application.getLogoutURL(); + if (logoutUrl == null) + logoutUrl = application.getURL().toString(); + // clients JS app is still running, send a special xml file to + // tell client that application is quit and where to point browser now + // Set the response type + response.setContentType("application/xml; charset=UTF-8"); + ServletOutputStream out = response.getOutputStream(); + out.println("<redirect url=\"" + logoutUrl + "\">"); + out.println("</redirect>"); + } + + /** + * Gets the Paintable Id. + * + * @param paintable + * @return the paintable Id. + */ + public synchronized String getPaintableId(Paintable paintable) { + + String id = (String) paintableIdMap.get(paintable); + if (id == null) { + id = "PID" + Integer.toString(idSequence++); + paintableIdMap.put(paintable, id); + idPaintableMap.put(id, paintable); + } + + return id; + } + + /** + * + * @return + */ + public synchronized Set getDirtyComponents() { + + // Remove unnecessary repaints from the list + Object[] paintables = dirtyPaintabletSet.toArray(); + for (int i = 0; i < paintables.length; i++) { + if (paintables[i] instanceof Component) { + Component c = (Component) paintables[i]; + + // Check if any of the parents of c already exist in the list + Component p = c.getParent(); + while (p != null) { + if (dirtyPaintabletSet.contains(p)) { + + // Remove component c from the dirty paintables as its + // parent is also dirty + dirtyPaintabletSet.remove(c); + p = null; + } else + p = p.getParent(); + } + } + } + + return Collections.unmodifiableSet(dirtyPaintabletSet); + } + + /** + * Clears the Dirty Components. + * + */ + public synchronized void clearDirtyComponents() { + dirtyPaintabletSet.clear(); + } + + /** + * @see com.itmill.toolkit.terminal.Paintable.RepaintRequestListener#repaintRequested(com.itmill.toolkit.terminal.Paintable.RepaintRequestEvent) + */ + public void repaintRequested(RepaintRequestEvent event) { + Paintable p = event.getPaintable(); + dirtyPaintabletSet.add(p); + + // For FrameWindows we mark all frames (windows) dirty + if (p instanceof FrameWindow) { + FrameWindow fw = (FrameWindow) p; + repaintFrameset(fw.getFrameset()); + } + } + + /** + * Recursively request repaint for all frames in frameset. + * + * @param fs + * the Framewindow.Frameset. + */ + private void repaintFrameset(FrameWindow.Frameset fs) { + List frames = fs.getFrames(); + for (Iterator i = frames.iterator(); i.hasNext();) { + FrameWindow.Frame f = (FrameWindow.Frame) i.next(); + if (f instanceof FrameWindow.Frameset) { + repaintFrameset((FrameWindow.Frameset) f); + } else { + Window w = f.getWindow(); + if (w != null) { + w.requestRepaint(); + } + } + } + } + + /** + * + * @param p + */ + public void paintablePainted(Paintable p) { + dirtyPaintabletSet.remove(p); + p.requestRepaintRequests(); + } + + /** + * + * @param paintable + * @return + */ + public boolean isDirty(Paintable paintable) { + return (dirtyPaintabletSet.contains(paintable)); + } + + /** + * @see com.itmill.toolkit.Application.WindowAttachListener#windowAttached(com.itmill.toolkit.Application.WindowAttachEvent) + */ + public void windowAttached(WindowAttachEvent event) { + event.getWindow().addListener(this); + dirtyPaintabletSet.add(event.getWindow()); + } + + /** + * @see com.itmill.toolkit.Application.WindowDetachListener#windowDetached(com.itmill.toolkit.Application.WindowDetachEvent) + */ + public void windowDetached(WindowDetachEvent event) { + event.getWindow().removeListener(this); + // Notify client of the close operation + removedWindows.add(event.getWindow()); + } + + /** + * + * @return + */ + public synchronized Set getRemovedWindows() { + return Collections.unmodifiableSet(removedWindows); + + } + + /** + * + * @param w + */ + private void removedWindowNotified(Window w) { + this.removedWindows.remove(w); + } + + private final class SingleValueMap implements Map { + private final String name; + + private final Object value; + + private SingleValueMap(String name, Object value) { + this.name = name; + this.value = value; + } + + public void clear() { + throw new UnsupportedOperationException(); + } + + public boolean containsKey(Object key) { + if (name == null) + return key == null; + return name.equals(key); + } + + public boolean containsValue(Object v) { + if (value == null) + return v == null; + return value.equals(v); + } + + public Set entrySet() { + Set s = new HashSet(); + s.add(new Map.Entry() { + + public Object getKey() { + return name; + } + + public Object getValue() { + return value; + } + + public Object setValue(Object value) { + throw new UnsupportedOperationException(); + } + }); + return s; + } + + public Object get(Object key) { + if (!name.equals(key)) + return null; + return value; + } + + public boolean isEmpty() { + return false; + } + + public Set keySet() { + Set s = new HashSet(); + s.add(name); + return s; + } + + public Object put(Object key, Object value) { + throw new UnsupportedOperationException(); + } + + public void putAll(Map t) { + throw new UnsupportedOperationException(); + } + + public Object remove(Object key) { + throw new UnsupportedOperationException(); + } + + public int size() { + return 1; + } + + public Collection values() { + LinkedList s = new LinkedList(); + s.add(value); + return s; + + } + } + + /** + * Implementation of URIHandler.ErrorEvent interface. + */ + public class URIHandlerErrorImpl implements URIHandler.ErrorEvent { + + private URIHandler owner; + + private Throwable throwable; + + /** + * + * @param owner + * @param throwable + */ + private URIHandlerErrorImpl(URIHandler owner, Throwable throwable) { + this.owner = owner; + this.throwable = throwable; + } + + /** + * @see com.itmill.toolkit.terminal.Terminal.ErrorEvent#getThrowable() + */ + public Throwable getThrowable() { + return this.throwable; + } + + /** + * @see com.itmill.toolkit.terminal.URIHandler.ErrorEvent#getURIHandler() + */ + public URIHandler getURIHandler() { + return this.owner; + } + } + + public void requireLocale(String value) { + if (locales == null) { + locales = new ArrayList(); + locales.add(application.getLocale().toString()); + pendingLocalesIndex = 0; + } + if (!locales.contains(value)) + locales.add(value); + } + + private Locale generateLocale(String value) { + String[] temp = value.split("_"); + if (temp.length == 1) + return new Locale(temp[0]); + else if (temp.length == 2) + return new Locale(temp[0], temp[1]); + else + return new Locale(temp[0], temp[1], temp[2]); + } +} |