From: Joonas Lehtinen Date: Tue, 17 Jul 2007 08:28:41 +0000 (+0000) Subject: Started a major refactoring: removed terminal.web, added terminal.gwt.server. Refacto... X-Git-Tag: 6.7.0.beta1~6168 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=511a2b97c27f986d0f6ee5231e76c913cf467f39;p=vaadin-framework.git Started a major refactoring: removed terminal.web, added terminal.gwt.server. Refactoring is not even nearly complete, but can already replace old web terminal implementation svn changeset:1864/svn branch:trunk --- diff --git a/WebContent/WEB-INF/web.xml b/WebContent/WEB-INF/web.xml index adad234158..21961382a7 100644 --- a/WebContent/WEB-INF/web.xml +++ b/WebContent/WEB-INF/web.xml @@ -13,7 +13,7 @@ SelectDemo - com.itmill.toolkit.terminal.web.ApplicationServlet + com.itmill.toolkit.terminal.gwt.server.ApplicationServlet application com.itmill.toolkit.demo.SelectDemo @@ -22,7 +22,7 @@ BufferedComponents - com.itmill.toolkit.terminal.web.ApplicationServlet + com.itmill.toolkit.terminal.gwt.server.ApplicationServlet application com.itmill.toolkit.demo.BufferedComponents @@ -31,7 +31,7 @@ TableDemo - com.itmill.toolkit.terminal.web.ApplicationServlet + com.itmill.toolkit.terminal.gwt.server.ApplicationServlet application com.itmill.toolkit.demo.TableDemo @@ -40,7 +40,7 @@ CustomLayoutDemo - com.itmill.toolkit.terminal.web.ApplicationServlet + com.itmill.toolkit.terminal.gwt.server.ApplicationServlet application com.itmill.toolkit.demo.CustomLayoutDemo @@ -49,7 +49,7 @@ LayoutDemo - com.itmill.toolkit.terminal.web.ApplicationServlet + com.itmill.toolkit.terminal.gwt.server.ApplicationServlet application com.itmill.toolkit.demo.LayoutDemo @@ -58,7 +58,7 @@ FilterSelect - com.itmill.toolkit.terminal.web.ApplicationServlet + com.itmill.toolkit.terminal.gwt.server.ApplicationServlet application com.itmill.toolkit.demo.FilterSelect @@ -67,7 +67,7 @@ FeaturesApplication - com.itmill.toolkit.terminal.web.ApplicationServlet + com.itmill.toolkit.terminal.gwt.server.ApplicationServlet application com.itmill.toolkit.demo.features.FeaturesApplication @@ -76,7 +76,7 @@ Parameters - com.itmill.toolkit.terminal.web.ApplicationServlet + com.itmill.toolkit.terminal.gwt.server.ApplicationServlet application com.itmill.toolkit.demo.Parameters @@ -85,7 +85,7 @@ UpgradingSample - com.itmill.toolkit.terminal.web.ApplicationServlet + com.itmill.toolkit.terminal.gwt.server.ApplicationServlet application com.itmill.toolkit.demo.UpgradingSample @@ -94,7 +94,7 @@ QueryContainerDemo - com.itmill.toolkit.terminal.web.ApplicationServlet + com.itmill.toolkit.terminal.gwt.server.ApplicationServlet application com.itmill.toolkit.demo.QueryContainerDemo @@ -103,7 +103,7 @@ TreeFilesystemContainer - com.itmill.toolkit.terminal.web.ApplicationServlet + com.itmill.toolkit.terminal.gwt.server.ApplicationServlet application com.itmill.toolkit.demo.TreeFilesystemContainer @@ -120,7 +120,7 @@ TreeFilesystem - com.itmill.toolkit.terminal.web.ApplicationServlet + com.itmill.toolkit.terminal.gwt.server.ApplicationServlet application com.itmill.toolkit.demo.TreeFilesystem @@ -137,7 +137,7 @@ ModalWindow - com.itmill.toolkit.terminal.web.ApplicationServlet + com.itmill.toolkit.terminal.gwt.server.ApplicationServlet application com.itmill.toolkit.demo.ModalWindow @@ -146,7 +146,7 @@ HelloWorld - com.itmill.toolkit.terminal.web.ApplicationServlet + com.itmill.toolkit.terminal.gwt.server.ApplicationServlet application com.itmill.toolkit.demo.HelloWorld @@ -155,7 +155,7 @@ Calc - com.itmill.toolkit.terminal.web.ApplicationServlet + com.itmill.toolkit.terminal.gwt.server.ApplicationServlet application com.itmill.toolkit.demo.Calc @@ -164,7 +164,7 @@ KeyboardShortcut - com.itmill.toolkit.terminal.web.ApplicationServlet + com.itmill.toolkit.terminal.gwt.server.ApplicationServlet application com.itmill.toolkit.demo.KeyboardShortcut diff --git a/src/com/itmill/toolkit/demo/features/IntroWelcome.java b/src/com/itmill/toolkit/demo/features/IntroWelcome.java index 75a643e1c4..89c0efb0cd 100644 --- a/src/com/itmill/toolkit/demo/features/IntroWelcome.java +++ b/src/com/itmill/toolkit/demo/features/IntroWelcome.java @@ -38,8 +38,8 @@ import com.itmill.toolkit.terminal.PaintException; import com.itmill.toolkit.terminal.PaintTarget; import com.itmill.toolkit.terminal.ParameterHandler; import com.itmill.toolkit.terminal.URIHandler; -import com.itmill.toolkit.terminal.web.ApplicationServlet; -import com.itmill.toolkit.terminal.web.WebBrowser; +import com.itmill.toolkit.terminal.gwt.server.ApplicationServlet; +import com.itmill.toolkit.terminal.gwt.server.WebBrowser; import com.itmill.toolkit.ui.Component; import com.itmill.toolkit.ui.Form; import com.itmill.toolkit.ui.Label; diff --git a/src/com/itmill/toolkit/terminal/gwt/server/AjaxHttpUploadStream.java b/src/com/itmill/toolkit/terminal/gwt/server/AjaxHttpUploadStream.java new file mode 100644 index 0000000000..e431d5126e --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/server/AjaxHttpUploadStream.java @@ -0,0 +1,114 @@ +/* ************************************************************************* + + 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.InputStream; + +/** + * AjaxAdapter implementation of the UploadStream interface. + * + * @author IT Mill Ltd. + * @version + * @VERSION@ + * @since 5.0 + */ +public class AjaxHttpUploadStream implements + com.itmill.toolkit.terminal.UploadStream { + + /** + * Holds value of property variableName. + */ + private String streamName; + + private String contentName; + + private String contentType; + + /** + * Holds value of property variableValue. + */ + private InputStream stream; + + /** + * Creates a new instance of UploadStreamImpl. + * + * @param name + * the name of the stream. + * @param stream + * the input stream. + * @param contentName + * the name of the content. + * @param contentType + * the type of the content. + */ + public AjaxHttpUploadStream(String name, InputStream stream, + String contentName, String contentType) { + this.streamName = name; + this.stream = stream; + this.contentName = contentName; + this.contentType = contentType; + } + + /** + * Gets the name of the stream. + * + * @return the name of the stream. + */ + public String getStreamName() { + return this.streamName; + } + + /** + * Gets the input stream. + * + * @return the Input stream. + */ + public InputStream getStream() { + return this.stream; + } + + /** + * Gets the input stream content type. + * + * @return the content type of the input stream. + */ + public String getContentType() { + return this.contentType; + } + + /** + * Gets the stream content name. Stream content name usually differs from + * the actual stream name. It is used to identify the content of the stream. + * + * @return the Name of the stream content. + */ + public String getContentName() { + return this.contentName; + } +} diff --git a/src/com/itmill/toolkit/terminal/gwt/server/AjaxVariableMap.java b/src/com/itmill/toolkit/terminal/gwt/server/AjaxVariableMap.java new file mode 100644 index 0000000000..54c0013361 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/server/AjaxVariableMap.java @@ -0,0 +1,803 @@ +/* ************************************************************************* + + 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.IOException; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.WeakHashMap; + +import javax.servlet.http.HttpServletRequest; + +import com.itmill.toolkit.terminal.SystemError; +import com.itmill.toolkit.terminal.Terminal; +import com.itmill.toolkit.terminal.UploadStream; +import com.itmill.toolkit.terminal.VariableOwner; + +/** + * Variable map for ajax applications. + * + * @author IT Mill Ltd. + * @version + * @VERSION@ + * @since 5.0 + */ +public class AjaxVariableMap { + + // Id <-> (Owner,Name) mapping + private Map idToNameMap = new HashMap(); + + private Map idToTypeMap = new HashMap(); + + private Map idToOwnerMap = new HashMap(); + + private Map idToValueMap = new HashMap(); + + private Map ownerToNameToIdMap = new WeakHashMap(); + + private Object mapLock = new Object(); + + // Id generator + private long lastId = 0; + + /** + * Converts the string to a supported class. + * + * @param type + * @param value + * @return + * @throws java.lang.ClassCastException + * if the code has attempted to cast an object to a subclass of + * which it is not an instance + */ + private static Object convert(Class type, String value) + throws java.lang.ClassCastException { + try { + + // Boolean typed variables + if (type.equals(Boolean.class)) + return new Boolean(!(value.equals("") || value.equals("false"))); + + // Integer typed variables + if (type.equals(Integer.class)) + return new Integer(value.trim()); + + // String typed variables + if (type.equals(String.class)) + return value; + + throw new ClassCastException("Unsupported type: " + type.getName()); + } catch (NumberFormatException e) { + return null; + } + } + + /** + * Registers a new variable. + * + * @param name + * the Variable name. + * @param type + * @param value + * @param owner + * the Listener for variable changes. + * @return id to assigned for this variable. + */ + public String registerVariable(String name, Class type, Object value, + VariableOwner owner) { + + // Checks that the type of the class is supported + if (!(type.equals(Boolean.class) || type.equals(Integer.class) + || type.equals(String.class) || type.equals(String[].class) || type + .equals(UploadStream.class))) + throw new SystemError("Unsupported variable type: " + + type.getClass()); + + synchronized (mapLock) { + + // Checks if the variable is already mapped + HashMap nameToIdMap = (HashMap) ownerToNameToIdMap.get(owner); + if (nameToIdMap == null) { + nameToIdMap = new HashMap(); + ownerToNameToIdMap.put(owner, nameToIdMap); + } + String id = (String) nameToIdMap.get(name); + + if (id == null) { + // Generates new id and register it + +// ---------- +// TODO This HACK is only included for testing GWT integration +//Original id = "v" + String.valueOf(++lastId); + Object pid = ApplicationManager.paintableIdMap.get(owner); + id = pid + "_"+name; +// ---------- + + + nameToIdMap.put(name, id); + idToOwnerMap.put(id, new WeakReference(owner)); + idToNameMap.put(id, name); + idToTypeMap.put(id, type); + } + + idToValueMap.put(id, value); + + return id; + } + } + + /** + * Unregisters the variable. + * + * @param name + * the Variable name. + * @param owner + * the Listener for variable changes. + */ + public void unregisterVariable(String name, VariableOwner owner) { + + synchronized (mapLock) { + + // Get the id + HashMap nameToIdMap = (HashMap) ownerToNameToIdMap.get(owner); + if (nameToIdMap == null) + return; + String id = (String) nameToIdMap.get(name); + if (id != null) + return; + + // Remove all the mappings + nameToIdMap.remove(name); + if (nameToIdMap.isEmpty()) + ownerToNameToIdMap.remove(owner); + idToNameMap.remove(id); + idToTypeMap.remove(id); + idToValueMap.remove(id); + idToOwnerMap.remove(id); + + } + } + + /** + * @author IT Mill Ltd. + * @version + * @VERSION@ + * @since 3.0 + */ + private class ParameterContainer { + + /** + * Constructs the mapping: listener to set of listened parameter names. + */ + private HashMap parameters = new HashMap(); + + /** + * Parameter values. + */ + private HashMap values = new HashMap(); + + /** + * Multipart parser used for parsing the request. + */ + private ServletMultipartRequest parser = null; + + /** + * Name - Value mapping of parameters that are not variables. + */ + private HashMap nonVariables = new HashMap(); + + /** + * Creates a new parameter container and parse the parameters from the + * request using GET, POST and POST/MULTIPART parsing + * + * @param req + * the Http request to handle. + * @throws IOException + * if the writing failed due to input/output error. + */ + public ParameterContainer(HttpServletRequest req) throws IOException { + // Parse GET / POST parameters + for (Enumeration e = req.getParameterNames(); e.hasMoreElements();) { + String paramName = (String) e.nextElement(); + String[] paramValues = req.getParameterValues(paramName); + addParam(paramName, paramValues); + } + + // Parse multipart variables + try { + parser = new ServletMultipartRequest(req, + MultipartRequest.MAX_READ_BYTES); + } catch (IllegalArgumentException ignored) { + parser = null; + } + + if (parser != null) { + for (Enumeration e = parser.getFileParameterNames(); e + .hasMoreElements();) { + String paramName = (String) e.nextElement(); + addParam(paramName, null); + } + for (Enumeration e = parser.getParameterNames(); e + .hasMoreElements();) { + String paramName = (String) e.nextElement(); + Enumeration val = parser.getURLParameters(paramName); + + // Create a linked list from enumeration to calculate + // elements + LinkedList l = new LinkedList(); + while (val.hasMoreElements()) + l.addLast(val.nextElement()); + + // String array event constructor + String[] s = new String[l.size()]; + Iterator i = l.iterator(); + for (int j = 0; j < s.length; j++) + s[j] = (String) i.next(); + + addParam(paramName, s); + } + } + + } + + /** + * Adds the parameter to container. + * + * @param name + * the Parameter name. + * @param value + * the Parameter value. + */ + private void addParam(String name, String[] value) { + + // Support name="set:name=value" value="ignored" notation + if (name.startsWith("set:")) { + int equalsIndex = name.indexOf('='); + value[0] = name.substring(equalsIndex + 1, name.length()); + name = name.substring(4, equalsIndex); + String[] curVal = (String[]) values.get(name); + if (curVal != null) { + String[] newVal = new String[1 + curVal.length]; + newVal[curVal.length] = value[0]; + for (int i = 0; i < curVal.length; i++) + newVal[i] = curVal[i]; + value = newVal; + + // Special case - if the set:-method is used for + // declaring array of length 2, where either of the + // following conditions are true: + // - the both items are the same + // - the both items have the same length and + // - the items only differ on last character + // - second last character is '.' + // - last char of one string is 'x' and other is 'y' + // Browser is unporposely modifying the name. + if (value.length == 2 + && value[0].length() == value[1].length()) { + boolean same = true; + for (int i = 0; i < value[0].length() - 1 && same; i++) + if (value[0].charAt(i) != value[1].charAt(i)) + same = false; + if (same + && ((value[0].charAt(value[0].length() - 1) == 'x' && value[1] + .charAt(value[1].length() - 1) == 'y') || (value[0] + .charAt(value[0].length() - 1) == 'y' && value[1] + .charAt(value[1].length() - 1) == 'x'))) { + value = new String[] { value[0].substring(0, + value[1].length() - 2) }; + } else if (same && value[0].equals(value[1])) + value = new String[] { value[0] }; + } + + // Special case - if the set:-method is used for + // declaring array of length 3, where all of the + // following conditions are true: + // - two last items have the same length + // - the first item is 2 chars shorter + // - the longer items only differ on last character + // - the shortest item is a prefix of the longer ones + // - second last character of longer ones is '.' + // - last char of one long string is 'x' and other is 'y' + // Browser is unporposely modifying the name. (Mozilla, + // Firefox, ..) + if (value.length == 3 + && value[1].length() == value[2].length() + && value[0].length() + 2 == value[1].length()) { + boolean same = true; + for (int i = 0; i < value[1].length() - 1 && same; i++) + if (value[2].charAt(i) != value[1].charAt(i)) + same = false; + for (int i = 0; i < value[0].length() && same; i++) + if (value[0].charAt(i) != value[1].charAt(i)) + same = false; + if (same + && (value[2].charAt(value[2].length() - 1) == 'x' && value[1] + .charAt(value[1].length() - 1) == 'y') + || (value[2].charAt(value[2].length() - 1) == 'y' && value[1] + .charAt(value[1].length() - 1) == 'x')) { + value = new String[] { value[0] }; + } + } + + } + } + + // Support for setting arrays in format + // set-array:name=value1,value2,value3,... + else if (name.startsWith("set-array:")) { + int equalsIndex = name.indexOf('='); + if (equalsIndex < 0) + return; + + StringTokenizer commalist = new StringTokenizer(name + .substring(equalsIndex + 1), ","); + name = name.substring(10, equalsIndex); + String[] curVal = (String[]) values.get(name); + ArrayList elems = new ArrayList(); + + // Add old values if present. + if (curVal != null) { + for (int i = 0; i < curVal.length; i++) + elems.add(curVal[i]); + } + while (commalist.hasMoreTokens()) { + String token = commalist.nextToken(); + if (token != null && token.length() > 0) + elems.add(token); + } + value = new String[elems.size()]; + for (int i = 0; i < value.length; i++) + value[i] = (String) elems.get(i); + + } + + // Support name="array:name" value="val1,val2,val3" notation + // All the empty elements are ignored + else if (name.startsWith("array:")) { + + name = name.substring(6); + StringTokenizer commalist = new StringTokenizer(value[0], ","); + String[] curVal = (String[]) values.get(name); + ArrayList elems = new ArrayList(); + + // Add old values if present. + if (curVal != null) { + for (int i = 0; i < curVal.length; i++) + elems.add(curVal[i]); + } + while (commalist.hasMoreTokens()) { + String token = commalist.nextToken(); + if (token != null && token.length() > 0) + elems.add(token); + } + value = new String[elems.size()]; + for (int i = 0; i < value.length; i++) + value[i] = (String) elems.get(i); + } + + // Support declaring variables with name="declare:name" + else if (name.startsWith("declare:")) { + name = name.substring(8); + value = (String[]) values.get(name); + if (value == null) + value = new String[0]; + } + + // Gets the owner + WeakReference ref = (WeakReference) idToOwnerMap.get(name); + VariableOwner owner = null; + if (ref != null) + owner = (VariableOwner) ref.get(); + + // Adds the parameter to mapping only if they have owners + if (owner != null) { + Set p = (Set) parameters.get(owner); + if (p == null) + parameters.put(owner, p = new HashSet()); + p.add(name); + if (value != null) + values.put(name, value); + } + + // If the owner can not be found + else { + + // If parameter has been mapped before, remove the old owner + // mapping + if (ref != null) { + + // The owner has been destroyed, so we remove the mappings + idToNameMap.remove(name); + idToOwnerMap.remove(name); + idToTypeMap.remove(name); + idToValueMap.remove(name); + } + + // Adds the parameter to set of non-variables + nonVariables.put(name, value); + } + + } + + /** + * Gets the set of all parameters connected to given variable owner. + * + * @param owner + * the Listener for variable changes. + * @return the set of all the parameters. + */ + public Set getParameters(VariableOwner owner) { + if (owner == null) + return null; + return (Set) parameters.get(owner); + } + + /** + * Gets the set of all variable owners owning parameters in this + * request. + * + * @return the set of all varaible owners. + */ + public Set getOwners() { + return parameters.keySet(); + } + + /** + * Gets the value of a parameter. + * + * @param parameterName + * the name of the parameter. + * @return the value of the parameter. + */ + public String[] getValue(String parameterName) { + return (String[]) values.get(parameterName); + } + + /** + * Gets the servlet multipart parser. + * + * @return the parser. + */ + public ServletMultipartRequest getParser() { + return parser; + } + + /** + * Gets the name - value[] mapping of non variable parameters. + * + * @return the mapping of non variable parameters. + */ + public Map getNonVariables() { + return nonVariables; + } + } + + /** + * Handles all variable changes in this request. + * + * @param req + * the Http request to handle. + * @param errorListener + * the listeners If the list is non null, only the listed + * listeners are served. Otherwise all the listeners are served. + * @return Name to Value[] mapping of unhandled variables. + * @throws IOException + * if the writing failed due to input/output error. + */ + public Map handleVariables(HttpServletRequest req, + Terminal.ErrorListener errorListener) throws IOException { + + // Gets the parameters + ParameterContainer parcon = new ParameterContainer(req); + + // Sorts listeners to dependency order + List listeners = getDependencySortedListenerList(parcon.getOwners()); + + // Handles all parameters for all listeners + while (!listeners.isEmpty()) { + VariableOwner listener = (VariableOwner) listeners.remove(0); + boolean changed = false; // Has any of this owners variabes + // changed + // Handles all parameters for listener + Set params = parcon.getParameters(listener); + if (params != null) { // Name value mapping + Map variables = new HashMap(); + for (Iterator pi = params.iterator(); pi.hasNext();) { + // Gets the name of the parameter + String param = (String) pi.next(); + // Extracts more information about the parameter + String varName = (String) idToNameMap.get(param); + Class varType = (Class) idToTypeMap.get(param); + Object varOldValue = idToValueMap.get(param); + if (varName == null || varType == null) + // TODO Remove this? + System.err + .println("VariableMap: No variable found for parameter " + + param + + " (" + + varName + + "," + + listener + ")"); + else { + + ServletMultipartRequest parser = parcon.getParser(); + + // Uploads events + if (varType.equals(UploadStream.class)) { + if (parser != null + && parser.getFileParameter(param, + MultipartRequest.FILENAME) != null) { + String filename = (String) parser + .getFileParameter(param, + MultipartRequest.FILENAME); + String contentType = (String) parser + .getFileParameter(param, + MultipartRequest.CONTENT_TYPE); + UploadStream upload = new AjaxHttpUploadStream( + varName, parser.getFileContents(param), + filename, contentType); + variables.put(varName, upload); + changed = true; + } + } + + // Normal variable change events + else { + // First try to parse the event without multipart + String[] values = parcon.getValue(param); + if (values != null) { + + if (varType.equals(String[].class)) { + variables.put(varName, values); + changed |= (!Arrays.equals(values, + (String[]) varOldValue)); + } else { + try { + if (values.length == 1) { + Object val = convert(varType, + values[0]); + variables.put(varName, val); + changed |= ((val == null && varOldValue != null) || (val != null && !val + .equals(varOldValue))); + } else if (values.length == 0 + && varType + .equals(Boolean.class)) { + Object val = new Boolean(false); + variables.put(varName, val); + changed |= (!val + .equals(varOldValue)); + } else { + // TODO Remove this? + System.err + .println("Empty variable '" + + varName + + "' of type " + + varType + .toString()); + } + + } catch (java.lang.ClassCastException e) { + // TODO Remove this? + System.err + .println("WebVariableMap conversion exception"); + e.printStackTrace(System.err); + errorListener + .terminalError(new TerminalErrorImpl( + e)); + } + } + } + } + } + } + + // Do the valuechange if the listener is enabled + if (listener.isEnabled() && changed) { + try { + listener.changeVariables(req, variables); + } catch (Throwable t) { + // Notify the error listener + errorListener.terminalError(new VariableOwnerErrorImpl( + listener, t)); + } + } + } + } + + return parcon.getNonVariables(); + } + + /** + * Implementation of VariableOwner.Error interface. + */ + public class TerminalErrorImpl implements Terminal.ErrorEvent { + private Throwable throwable; + + /** + * + * @param throwable + */ + private TerminalErrorImpl(Throwable throwable) { + this.throwable = throwable; + } + + /** + * @see com.itmill.toolkit.terminal.Terminal.ErrorEvent#getThrowable() + */ + public Throwable getThrowable() { + return this.throwable; + } + + } + + /** + * Implementation of VariableOwner.Error interface. + */ + public class VariableOwnerErrorImpl extends TerminalErrorImpl implements + VariableOwner.ErrorEvent { + + private VariableOwner owner; + + /** + * + * @param owner + * the Listener for variable changes. + * @param throwable + */ + private VariableOwnerErrorImpl(VariableOwner owner, Throwable throwable) { + super(throwable); + this.owner = owner; + } + + /** + * @see com.itmill.toolkit.terminal.VariableOwner.ErrorEvent#getVariableOwner() + */ + public VariableOwner getVariableOwner() { + return this.owner; + } + + } + + /** + * Resolves the VariableOwners needed from the request and sort them to + * assure that the dependencies are met (as well as possible). + * + * @param listeners + * @return List of variable list changers, that are needed for handling all + * the variables in the request + */ + private List getDependencySortedListenerList(Set listeners) { + + LinkedList resultNormal = new LinkedList(); + LinkedList resultImmediate = new LinkedList(); + + // Go trough the listeners and either add them to result or resolve + // their dependencies + HashMap deepdeps = new HashMap(); + LinkedList unresolved = new LinkedList(); + for (Iterator li = listeners.iterator(); li.hasNext();) { + + VariableOwner listener = (VariableOwner) li.next(); + if (listener != null) { + Set dependencies = listener.getDirectDependencies(); + + // The listeners with no dependencies are added to the front of + // the + // list directly + if (dependencies == null || dependencies.isEmpty()) { + if (listener.isImmediate()) + resultImmediate.addFirst(listener); + else + resultNormal.addFirst(listener); + } + + // Resolve deep dependencies for the listeners with dependencies + // (the listeners will be added to the end of results in correct + // dependency order later). Also the dependencies of all the + // depended listeners are resolved. + else if (deepdeps.get(listener) == null) { + + // Set the fifo for unresolved parents to contain only the + // listener to be resolved + unresolved.clear(); + unresolved.add(listener); + + // Resolve dependencies + HashSet tmpdeepdeps = new HashSet(); + while (!unresolved.isEmpty()) { + + VariableOwner l = (VariableOwner) unresolved + .removeFirst(); + if (!tmpdeepdeps.contains(l)) { + tmpdeepdeps.add(l); + if (deepdeps.containsKey(l)) { + tmpdeepdeps.addAll((Set) deepdeps.get(l)); + } else { + Set deps = l.getDirectDependencies(); + if (deps != null && !deps.isEmpty()) + for (Iterator di = deps.iterator(); di + .hasNext();) { + Object d = di.next(); + if (d != null + && !tmpdeepdeps.contains(d)) + unresolved.addLast(d); + } + } + } + } + + tmpdeepdeps.remove(listener); + deepdeps.put(listener, tmpdeepdeps); + } + } + } + + // Adds the listeners with dependencies in sane order to the result + for (Iterator li = deepdeps.keySet().iterator(); li.hasNext();) { + VariableOwner l = (VariableOwner) li.next(); + boolean immediate = l.isImmediate(); + + // Adds each listener after the last depended listener already in + // the list + int index = -1; + for (Iterator di = ((Set) deepdeps.get(l)).iterator(); di.hasNext();) { + int k; + Object depended = di.next(); + if (immediate) { + k = resultImmediate.lastIndexOf(depended); + } else { + k = resultNormal.lastIndexOf(depended); + } + if (k > index) + index = k; + } + if (immediate) { + resultImmediate.add(index + 1, l); + } else { + resultNormal.add(index + 1, l); + } + } + + // Appends immediate listeners to normal listeners + // This way the normal handlers are always called before + // immediate ones + resultNormal.addAll(resultImmediate); + return resultNormal; + } +} diff --git a/src/com/itmill/toolkit/terminal/gwt/server/ApplicationManager.java b/src/com/itmill/toolkit/terminal/gwt/server/ApplicationManager.java new file mode 100644 index 0000000000..3a3c27af53 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/server/ApplicationManager.java @@ -0,0 +1,929 @@ +/* ************************************************************************* + + 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.Collections; +import java.util.Comparator; +import java.util.GregorianCalendar; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +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.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 ApplicationManager 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(); + + + // TODO THIS TEMPORARY HACK IS ONLY HERE TO MAKE GWT DEVEL EASIER + static WeakHashMap paintableIdMap = 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 ApplicationManager(Application application, ApplicationServlet applicationServlet) { + this.application = application; + this.applicationServlet = applicationServlet; + requireLocale(application.getLocale().toString()); + } + + /** + * + * @return + */ + private AjaxVariableMap getVariableMap() { + AjaxVariableMap vm = (AjaxVariableMap) applicationToVariableMapMap + .get(application); + if (vm == null) { + vm = new AjaxVariableMap(); + applicationToVariableMapMap.put(application, vm); + } + return vm; + } + + /** + * + * + */ + 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"))); + + 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 = getVariableMap().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(getVariableMap(), + 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 + + + // Render the removed windows + // TODO refactor commented area to send some meta instructions to close window +// Set removed = new HashSet(getRemovedWindows()); +// if (removed.size() > 0) { +// for (Iterator i = removed.iterator(); i.hasNext();) { +// Window w = (Window) i.next(); +// paintTarget.startTag("change"); +// paintTarget.addAttribute("format", "uidl"); +// String pid = getPaintableId(w); +// paintTarget.addAttribute("pid", pid); +// paintTarget.addAttribute("windowname", w.getName()); +// paintTarget.addAttribute("visible", false); +// paintTarget.endTag("change"); +// removedWindowNotified(w); +// +// } +// } + + + + 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("}"); + + + /* ----------------------------- + * 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); + + 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("Application Internal Error"); + err.write("

" + e.toString() + "

\n");
+			e.printStackTrace(new PrintWriter(err));
+			err.write("\n
"); + err.close(); + } finally { + + } + + } + + /** + * 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 true if the request was handled and + * further processing should be suppressed, otherwise + * false. + * @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(""); + out.println(""); + } + + /** + * 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); + } + + 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); + } + + /** + * 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]); + } +} diff --git a/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java b/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java new file mode 100644 index 0000000000..62c561b480 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java @@ -0,0 +1,1423 @@ +/* ************************************************************************* + + 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.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.WeakHashMap; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpSessionBindingEvent; +import javax.servlet.http.HttpSessionBindingListener; + +import org.xml.sax.SAXException; + +import com.itmill.toolkit.Application; +import com.itmill.toolkit.service.FileTypeResolver; +import com.itmill.toolkit.service.License; +import com.itmill.toolkit.service.License.InvalidLicenseFile; +import com.itmill.toolkit.service.License.LicenseFileHasNotBeenRead; +import com.itmill.toolkit.service.License.LicenseSignatureIsInvalid; +import com.itmill.toolkit.service.License.LicenseViolation; +import com.itmill.toolkit.terminal.DownloadStream; +import com.itmill.toolkit.terminal.Paintable; +import com.itmill.toolkit.terminal.ParameterHandler; +import com.itmill.toolkit.terminal.ThemeResource; +import com.itmill.toolkit.terminal.URIHandler; +import com.itmill.toolkit.ui.Window; + +/** + * This servlet connects IT Mill Toolkit Application to Web. This servlet + * replaces both WebAdapterServlet and AjaxAdapterServlet. + * + * @author IT Mill Ltd. + * @version + * @VERSION@ + * @since 4.0 + */ + +public class ApplicationServlet extends HttpServlet { + + private static final long serialVersionUID = -4937882979845826574L; + + /** + * Version number of this release. For example "4.0.0". + */ + public static final String VERSION; + + /** + * Major version number. For example 4 in 4.1.0. + */ + public static final int VERSION_MAJOR; + + /** + * Minor version number. For example 1 in 4.1.0. + */ + public static final int VERSION_MINOR; + + /** + * Builds number. For example 0-beta1 in 4.0.0-beta1. + */ + public static final String VERSION_BUILD; + + /* Initialize version numbers from string replaced by build-script. */ + static { + if ("@VERSION@".equals("@" + "VERSION" + "@")) + VERSION = "4.9.9-INTERNAL-NONVERSIONED-DEBUG-BUILD"; + else + VERSION = "@VERSION@"; + String[] digits = VERSION.split("\\."); + VERSION_MAJOR = Integer.parseInt(digits[0]); + VERSION_MINOR = Integer.parseInt(digits[1]); + VERSION_BUILD = digits[2]; + } + + // Configurable parameter names + private static final String PARAMETER_DEBUG = "Debug"; + + private static final int DEFAULT_BUFFER_SIZE = 32 * 1024; + + private static final int MAX_BUFFER_SIZE = 64 * 1024; + + // TODO: these should be moved to session object and stored directly into + // session + private static final String SESSION_ATTR_VARMAP = "itmill-toolkit-varmap"; + + private static final String SESSION_ATTR_CONTEXT = "itmill-toolkit-context"; + + protected static final String SESSION_ATTR_APPS = "itmill-toolkit-apps"; + + private static final String SESSION_BINDING_LISTENER = "itmill-toolkit-bindinglistener"; + + private static HashMap applicationToLastRequestDate = new HashMap(); + + private static HashMap applicationToAjaxAppMgrMap = new HashMap(); + + // License for ApplicationServlets + private static HashMap licenseForApplicationClass = new HashMap(); + + private static HashMap licensePrintedForApplicationClass = new HashMap(); + + // TODO Should default or base theme be the default? + protected static final String DEFAULT_THEME = "base"; + + private static final String RESOURCE_URI = "/RES/"; + + private static final String AJAX_UIDL_URI = "/UIDL/"; + + static final String THEME_DIRECTORY_PATH = "/theme/"; + + // Maximum delay between request for an user to be considered active (in ms) + private static final long ACTIVE_USER_REQUEST_INTERVAL = 1000 * 45; + + private static final int DEFAULT_THEME_CACHETIME = 1000 * 60 * 60 * 24; + // Private fields + private Class applicationClass; + + private Properties applicationProperties; + + private String resourcePath = null; + + private String debugMode = ""; + + /** + * Called by the servlet container to indicate to a servlet that the servlet + * is being placed into service. + * + * @param servletConfig + * the object containing the servlet's configuration and + * initialization parameters + * @throws javax.servlet.ServletException + * if an exception has occurred that interferes with the + * servlet's normal operation. + */ + public void init(javax.servlet.ServletConfig servletConfig) + throws javax.servlet.ServletException { + super.init(servletConfig); + + // Gets the application class name + String applicationClassName = servletConfig + .getInitParameter("application"); + if (applicationClassName == null) { + Log.error("Application not specified in servlet parameters"); + } + + // Stores the application parameters into Properties object + this.applicationProperties = new Properties(); + for (Enumeration e = servletConfig.getInitParameterNames(); e + .hasMoreElements();) { + String name = (String) e.nextElement(); + this.applicationProperties.setProperty(name, servletConfig + .getInitParameter(name)); + } + + // Overrides with server.xml parameters + ServletContext context = servletConfig.getServletContext(); + for (Enumeration e = context.getInitParameterNames(); e + .hasMoreElements();) { + String name = (String) e.nextElement(); + this.applicationProperties.setProperty(name, context + .getInitParameter(name)); + } + + // Gets the debug window parameter + String debug = getApplicationOrSystemProperty(PARAMETER_DEBUG, "") + .toLowerCase(); + + // Enables application specific debug + if (!"".equals(debug) && !"true".equals(debug) + && !"false".equals(debug)) + throw new ServletException( + "If debug parameter is given for an application, it must be 'true' or 'false'"); + this.debugMode = debug; + + // Loads the application class using the same class loader + // as the servlet itself + ClassLoader loader = this.getClass().getClassLoader(); + try { + this.applicationClass = loader.loadClass(applicationClassName); + } catch (ClassNotFoundException e) { + throw new ServletException("Failed to load application class: " + + applicationClassName); + } + } + + /** + * Gets an application or system property value. + * + * @param parameterName + * the Name or the parameter. + * @param defaultValue + * the Default to be used. + * @return String value or default if not found + */ + private String getApplicationOrSystemProperty(String parameterName, + String defaultValue) { + + // Try application properties + String val = this.applicationProperties.getProperty(parameterName); + if (val != null) { + return val; + } + + // Try lowercased application properties for backward compability with + // 3.0.2 and earlier + val = this.applicationProperties.getProperty(parameterName + .toLowerCase()); + if (val != null) { + return val; + } + + // Try system properties + String pkgName; + Package pkg = this.getClass().getPackage(); + if (pkg != null) { + pkgName = pkg.getName(); + } else { + String className = this.getClass().getName(); + pkgName = new String(className.toCharArray(), 0, className + .lastIndexOf('.')); + } + val = System.getProperty(pkgName + "." + parameterName); + if (val != null) { + return val; + } + + // Try lowercased system properties + val = System.getProperty(pkgName + "." + parameterName.toLowerCase()); + if (val != null) { + return val; + } + + return defaultValue; + } + + /** + * Receives standard HTTP requests from the public service method and + * dispatches them. + * + * @param request + * the object that contains the request the client made of the + * servlet. + * @param response + * the object that contains the response the servlet returns to + * the client. + * @throws ServletException + * if an input or output error occurs while the servlet is + * handling the TRACE request. + * @throws IOException + * if the request for the TRACE cannot be handled. + */ + protected void service(HttpServletRequest request, + HttpServletResponse response) throws ServletException, IOException { + + // Transformer and output stream for the result + HttpVariableMap variableMap = null; + OutputStream out = response.getOutputStream(); + Application application = null; + try { + + // Gets the application + application = getApplication(request); + + // Creates application if it doesn't exist + if (application == null) + application = createApplication(request); + + // Sets the last application request date + synchronized (applicationToLastRequestDate) { + applicationToLastRequestDate.put(application, new Date()); + } + + // Invokes context transaction listeners + ((WebApplicationContext) application.getContext()) + .startTransaction(application, request); + + // 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) { + + // Handles AJAX UIDL requests + String resourceId = request.getPathInfo(); + if (resourceId != null && resourceId.startsWith(AJAX_UIDL_URI)) { + getApplicationManager(application).handleUidlRequest( + request, response); + return; + } + + // Gets the variable map + variableMap = getVariableMap(application, request); + if (variableMap == null) + return; + + // Change all variables based on request parameters + Map unhandledParameters = variableMap.handleVariables(request, + application); + + // Check/handle client side feature checks + WebBrowserProbe + .handleProbeRequest(request, unhandledParameters); + + // If rendering mode is not defined or detecting requested + // try to detect it + WebBrowser wb = WebBrowserProbe.getTerminalType(request + .getSession()); + + boolean detect = false; + if (unhandledParameters.get("renderingMode") != null) { + detect = ((String) ((Object[]) unhandledParameters + .get("renderingMode"))[0]).equals("detect"); + } + if (detect) { + String themeName = application.getTheme(); + if (themeName == null) + themeName = DEFAULT_THEME; + if (unhandledParameters.get("theme") != null) { + themeName = (String) ((Object[]) unhandledParameters + .get("theme"))[0]; + } + } + + // 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) { + + // Window renders are not cacheable + response.setHeader("Cache-Control", "no-cache"); + response.setHeader("Pragma", "no-cache"); + response.setDateHeader("Expires", 0); + + // Finds the window within the application + Window window = null; + if (application.isRunning()) + window = getApplicationWindow(request, application, + unhandledParameters); + + // Handles the unhandled parameters if the application is + // still running + if (window != null && unhandledParameters != null + && !unhandledParameters.isEmpty()) { + try { + window.handleParameters(unhandledParameters); + } catch (Throwable t) { + application + .terminalError(new ParameterHandlerErrorImpl( + window, t)); + } + } + + // Removes application if it has stopped + if (!application.isRunning()) { + endApplication(request, response, application); + return; + } + + // Returns blank page, if no window found + if (window == null) { + response.setContentType("text/html"); + BufferedWriter page = new BufferedWriter( + new OutputStreamWriter(out)); + page.write(""); + page + .write("The requested window has been removed from application."); + page.write(""); + page.close(); + + return; + } + + // Sets terminal type for the window, if not already set + if (window.getTerminal() == null) { + window.setTerminal(wb); + } + + // Finds theme + String themeName = window.getTheme() != null ? window + .getTheme() : DEFAULT_THEME; + if (unhandledParameters.get("theme") != null) { + themeName = (String) ((Object[]) unhandledParameters + .get("theme"))[0]; + } + + // Handles resource requests + if (handleResourceRequest(request, response, themeName)) + return; + + + writeAjaxPage(request, response, out, + unhandledParameters, window, wb, themeName); + } + } + + // For normal requests, transform the window + if (download != null) + + handleDownload(download, request, response); + + + + } catch (Throwable e) { + // Print stacktrace + e.printStackTrace(); + // Re-throw other exceptions + throw new ServletException(e); + } finally { + + // Notifies transaction end + if (application != null) + ((WebApplicationContext) application.getContext()) + .endTransaction(application, request); + } + } + + /** + * + * @param request + * the HTTP request. + * @param response + * the HTTP response to write to. + * @param out + * @param unhandledParameters + * @param window + * @param terminalType + * @param theme + * @throws IOException + * if the writing failed due to input/output error. + * @throws MalformedURLException + * if the application is denied access the persistent data store + * represented by the given URL. + */ + private void writeAjaxPage(HttpServletRequest request, + HttpServletResponse response, OutputStream out, + Map unhandledParameters, Window window, WebBrowser terminalType, String themeName) throws IOException, MalformedURLException { + response.setContentType("text/html"); + BufferedWriter page = new BufferedWriter(new OutputStreamWriter(out)); + + page + .write("\n"); + + + + page.write("\n\nIT Mill Toolkit 5\n" + + "\n" + + "\n" + + "" + + "\n\n\n" + + " \n" + + "
" + + "
\n" + + "
console
"+ + "
IT Mill Toolkit 5 Prototype
\n" + + " \n" + + "\n"); + + + +// Theme t = theme; +// Vector themes = new Vector(); +// themes.add(t); +// while (t.getParent() != null) { +// String parentName = t.getParent(); +// t = themeSource.getThemeByName(parentName); +// themes.add(t); +// } +// for (int k = themes.size() - 1; k >= 0; k--) { +// t = (Theme) themes.get(k); +// Collection files = t.getFileNames(terminalType, Theme.MODE_AJAX); +// for (Iterator i = files.iterator(); i.hasNext();) { +// String file = (String) i.next(); +// if (file.endsWith(".css")) +// page.write("\n"); +// else if (file.endsWith(".js")) { +// page.write("\n"); +// } +// } +// +// } + + +// page.write("itmill.tmp = new itmill.Client(" +// + "document.getElementById('ajax-window')," + "\"" + appUrl +// + "/UIDL/" + "\",\"" + resourcePath +// + ((Theme) themes.get(themes.size() - 1)).getName() + "/" +// +// + "client/\",document.getElementById('ajax-wait'));\n"); + + // TODO Only current theme is registered to the client + // for (int k = themes.size() - 1; k >= 0; k--) { + // t = (Theme) themes.get(k); +// t = theme; +// String themeObjName = "itmill.themes." +// + t.getName().substring(0, 1).toUpperCase() +// + t.getName().substring(1); +// page.write(" (new " + themeObjName + "(\"" + resourcePath + t.getName() +// + "/\")).registerTo(itmill.tmp);\n"); + // } + + page.close(); + } + + /** + * 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 true if the request was handled and + * further processing should be suppressed, false + * otherwise. + * @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; + + // Removes the leading / + while (uri.startsWith("/") && uri.length() > 0) + uri = uri.substring(1); + + // Handles 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 download 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) { + } + + } + + } + + /** + * Handles theme resource file requests. Resources supplied with the themes + * are provided by the WebAdapterServlet. + * + * @param request + * the HTTP request. + * @param response + * the HTTP response. + * @return boolean true if the request was handled and + * further processing should be suppressed, false + * otherwise. + * @throws ServletException + * if an exception has occurred that interferes with the + * servlet's normal operation. + */ + private boolean handleResourceRequest(HttpServletRequest request, + HttpServletResponse response, String themeName) throws ServletException { + + // If the resource path is unassigned, initialize it + if (resourcePath == null) { + resourcePath = request.getContextPath() + request.getServletPath() + + RESOURCE_URI; + // WebSphere Application Server related fix + resourcePath = resourcePath.replaceAll("//", "/"); + } + + String resourceId = request.getPathInfo(); + + // Checks if this really is a resource request + if (resourceId == null || !resourceId.startsWith(RESOURCE_URI)) + return false; + + // Checks the resource type + resourceId = resourceId.substring(RESOURCE_URI.length()); + InputStream data = null; + + // Gets theme resources + try { + data = getServletContext().getResourceAsStream(THEME_DIRECTORY_PATH + themeName + "/" + resourceId); + } catch (Exception e) { + Log.info(e.getMessage()); + data = null; + } + + // Writes the response + try { + if (data != null) { + response.setContentType(FileTypeResolver + .getMIMEType(resourceId)); + + // Use default cache time for theme resources + response.setHeader("Cache-Control", "max-age=" + + DEFAULT_THEME_CACHETIME / 1000); + response.setDateHeader("Expires", System + .currentTimeMillis() + + DEFAULT_THEME_CACHETIME); + response.setHeader("Pragma", "cache"); // Required to apply + // caching in some + // Tomcats + + // Writes the data to client + byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; + int bytesRead = 0; + OutputStream out = response.getOutputStream(); + while ((bytesRead = data.read(buffer)) > 0) { + out.write(buffer, 0, bytesRead); + } + out.close(); + data.close(); + } else { + response.sendError(HttpServletResponse.SC_NOT_FOUND); + } + + } catch (java.io.IOException e) { + Log.info("Resource transfer failed: " + request.getRequestURI() + + ". (" + e.getMessage() + ")"); + } + + return true; + } + + /** + * Gets the variable map for the session. + * + * @param application + * @param request + * the HTTP request. + * @return the variable map. + * + */ + private static synchronized HttpVariableMap getVariableMap( + Application application, HttpServletRequest request) { + + HttpSession session = request.getSession(); + + // Gets the application to variablemap map + Map varMapMap = (Map) session.getAttribute(SESSION_ATTR_VARMAP); + if (varMapMap == null) { + varMapMap = new WeakHashMap(); + session.setAttribute(SESSION_ATTR_VARMAP, varMapMap); + } + + // Creates a variable map, if it does not exists. + HttpVariableMap variableMap = (HttpVariableMap) varMapMap + .get(application); + if (variableMap == null) { + variableMap = new HttpVariableMap(); + varMapMap.put(application, variableMap); + } + + return variableMap; + } + + /** + * Gets the current application URL from request. + * + * @param request + * the HTTP request. + * @throws MalformedURLException + * if the application is denied access to the persistent data + * store represented by the given URL. + */ + private URL getApplicationUrl(HttpServletRequest request) + throws MalformedURLException { + + URL applicationUrl; + try { + URL reqURL = new URL( + (request.isSecure() ? "https://" : "http://") + + request.getServerName() + + ((request.isSecure() && request.getServerPort() == 443) + || (!request.isSecure() && request + .getServerPort() == 80) ? "" : ":" + + request.getServerPort()) + + request.getRequestURI()); + String servletPath = request.getContextPath() + + request.getServletPath(); + if (servletPath.length() == 0 + || servletPath.charAt(servletPath.length() - 1) != '/') + servletPath = servletPath + "/"; + applicationUrl = new URL(reqURL, servletPath); + } catch (MalformedURLException e) { + Log.error("Error constructing application url " + + request.getRequestURI() + " (" + e + ")"); + throw e; + } + + return applicationUrl; + } + + /** + * Gets the existing application for given request. Looks for application + * instance for given request based on the requested URL. + * + * @param request + * the HTTP request. + * @return Application instance, or null if the URL does not map to valid + * application. + * @throws MalformedURLException + * if the application is denied access to the persistent data + * store represented by the given URL. + */ + private Application getApplication(HttpServletRequest request) + throws MalformedURLException { + + // Ensures that the session is still valid + HttpSession session = request.getSession(false); + if (session == null) + return null; + + // Gets application list for the session. + LinkedList applications = (LinkedList) session + .getAttribute(SESSION_ATTR_APPS); + if (applications == null) + return null; + + // Search for the application (using the application URI) from the list + Application application = null; + for (Iterator i = applications.iterator(); i.hasNext() + && application == null;) { + Application a = (Application) i.next(); + String aPath = a.getURL().getPath(); + String servletPath = request.getContextPath() + + request.getServletPath(); + if (servletPath.length() < aPath.length()) + servletPath += "/"; + if (servletPath.equals(aPath)) + application = a; + } + + // Removes stopped applications from the list + if (application != null && !application.isRunning()) { + applications.remove(application); + application = null; + } + + return application; + } + + /** + * Creates a new application. + * + * @param request + * the HTTP request. + * @return the New application instance. + * @throws MalformedURLException + * if the application is denied access to the persistent data + * store represented by the given URL. + * @throws InstantiationException + * if a new instance of the class cannot be instantiated. + * @throws IllegalAccessException + * if it does not have access to the property accessor method. + * @throws LicenseFileHasNotBeenRead + * if the license file has not been read. + * @throws LicenseSignatureIsInvalid + * if the license file has been changed or signature is + * otherwise invalid. + * @throws InvalidLicenseFile + * if the license file is not of correct XML format. + * @throws LicenseViolation + * + * @throws SAXException + * the Error parsing the license file. + */ + private Application createApplication(HttpServletRequest request) + throws MalformedURLException, InstantiationException, + IllegalAccessException, LicenseFileHasNotBeenRead, + LicenseSignatureIsInvalid, InvalidLicenseFile, LicenseViolation, + SAXException { + + Application application = null; + + // Gets the application url + URL applicationUrl = getApplicationUrl(request); + + // Gets application list. + HttpSession session = request.getSession(); + if (session == null) + return null; + LinkedList applications = (LinkedList) session + .getAttribute(SESSION_ATTR_APPS); + if (applications == null) { + applications = new LinkedList(); + session.setAttribute(SESSION_ATTR_APPS, applications); + HttpSessionBindingListener sessionBindingListener = new SessionBindingListener( + applications); + session.setAttribute(SESSION_BINDING_LISTENER, + sessionBindingListener); + } + + // Creates new application and start it + try { + application = (Application) this.applicationClass.newInstance(); + applications.add(application); + + // Sets locale + application.setLocale(request.getLocale()); + + // Gets application context for this session + WebApplicationContext context = (WebApplicationContext) session + .getAttribute(SESSION_ATTR_CONTEXT); + if (context == null) { + context = new WebApplicationContext(session); + session.setAttribute(SESSION_ATTR_CONTEXT, context); + } + + // Starts application and check license + initializeLicense(application); + application.start(applicationUrl, this.applicationProperties, + context); + checkLicense(application); + + } catch (IllegalAccessException e) { + Log.error("Illegal access to application class " + + this.applicationClass.getName()); + throw e; + } catch (InstantiationException e) { + Log.error("Failed to instantiate application class: " + + this.applicationClass.getName()); + throw e; + } + + return application; + } + + /** + * + * @param application + */ + private void initializeLicense(Application application) { + License license; + synchronized (licenseForApplicationClass) { + license = (License) licenseForApplicationClass.get(application + .getClass()); + if (license == null) { + license = new License(); + licenseForApplicationClass.put(application.getClass(), license); + } + } + application.setToolkitLicense(license); + } + + /** + * + * @param application + * @throws LicenseFileHasNotBeenRead + * if the license file has not been read. + * @throws LicenseSignatureIsInvalid + * if the license file has been changed or signature is + * otherwise invalid. + * @throws InvalidLicenseFile + * if the license file is not of correct XML format. + * @throws LicenseViolation + * + * @throws SAXException + * the Error parsing the license file. + */ + private void checkLicense(Application application) + throws LicenseFileHasNotBeenRead, LicenseSignatureIsInvalid, + InvalidLicenseFile, LicenseViolation, SAXException { + License license = application.getToolkitLicense(); + + if (!license.hasBeenRead()) + // Lock threads that have not yet read license + synchronized (license) { + if (!license.hasBeenRead()) { + InputStream lis; + try { + URL url = getServletContext().getResource( + "/WEB-INF/itmill-toolkit-license.xml"); + if (url == null) { + throw new RuntimeException( + "License file could not be read. " + + "You can install it to " + + "WEB-INF/itmill-toolkit-license.xml."); + } + lis = url.openStream(); + license.readLicenseFile(lis); + } catch (MalformedURLException e) { + // This should not happen + throw new RuntimeException(e); + } catch (IOException e) { + // This should not happen + throw new RuntimeException(e); + } + + // For each application class, print license description - + // once + if (!licensePrintedForApplicationClass + .containsKey(applicationClass)) { + licensePrintedForApplicationClass.put(applicationClass, + Boolean.TRUE); + if (license.shouldLimitsBePrintedOnInit()) { + System.out.println(license + .getDescription(application.getClass() + .toString())); + } + } + + // Checks license validity + try { + license.check(applicationClass, VERSION_MAJOR, + VERSION_MINOR, "IT Mill Toolkit", null); + } catch (LicenseFileHasNotBeenRead e) { + application.close(); + throw e; + } catch (LicenseSignatureIsInvalid e) { + application.close(); + throw e; + } catch (InvalidLicenseFile e) { + application.close(); + throw e; + } catch (LicenseViolation e) { + application.close(); + throw e; + } + } + } + + // Checks concurrent user limit + try { + license.checkConcurrentUsers(getNumberOfActiveUsers() + 1); + } catch (LicenseViolation e) { + application.close(); + throw e; + } + } + + /** + * Gets the number of active application-user pairs. + * + * This returns total number of all applications in the server that are + * considered to be active. For an application to be active, it must have + * been accessed less than ACTIVE_USER_REQUEST_INTERVAL ms. + * + * @return the Number of active application instances in the server. + */ + private int getNumberOfActiveUsers() { + int active = 0; + + synchronized (applicationToLastRequestDate) { + Set apps = applicationToLastRequestDate.keySet(); + long now = System.currentTimeMillis(); + for (Iterator i = apps.iterator(); i.hasNext();) { + Date lastReq = (Date) applicationToLastRequestDate + .get(i.next()); + if (now - lastReq.getTime() < ACTIVE_USER_REQUEST_INTERVAL) + active++; + } + } + + return active; + } + + /** + * Ends the application. + * + * @param request + * the HTTP request. + * @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(); + + HttpSession session = request.getSession(); + if (session != null) { + LinkedList applications = (LinkedList) session + .getAttribute(SESSION_ATTR_APPS); + if (applications != null) + applications.remove(application); + } + + response.sendRedirect(response.encodeRedirectURL(logoutUrl)); + } + + /** + * 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, Map params) throws ServletException { + + Window window = null; + + // Finds 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); + + if (window == null) { + // By default, we use main window + window = application.getMainWindow(); + } else if (!window.isVisible()) { + // Implicitly painting without actually invoking paint() + window.requestRepaintRequests(); + + // If the window is invisible send a blank page + return null; + } + } + + return window; + } + + /** + * Gets relative location of a theme resource. + * + * @param theme + * the Theme name. + * @param resource + * the Theme resource. + * @return External URI specifying the resource + */ + public String getResourceLocation(String theme, ThemeResource resource) { + + if (resourcePath == null) + return resource.getResourceId(); + return resourcePath + theme + "/" + resource.getResourceId(); + } + + /** + * Checks if web adapter is in debug mode. Extra output is generated to log + * when debug mode is enabled. + * + * @param parameters + * @return true if the web adapter is in debug mode. + * otherwise false. + */ + public boolean isDebugMode(Map parameters) { + if (parameters != null) { + Object[] debug = (Object[]) parameters.get("debug"); + if (debug != null && !"false".equals(debug[0].toString()) + && !"false".equals(debugMode)) + return true; + } + return "true".equals(debugMode); + } + + /** + * + * SessionBindingListener performs Application cleanups after sessions are + * expired. For each session exists one SessionBindingListener. It contains + * references to all applications related to single session. + * + * @author IT Mill Ltd. + * @version + * @VERSION@ + * @since 4.0 + */ + + private class SessionBindingListener implements HttpSessionBindingListener { + private LinkedList applications; + + /** + * + * @param applications + */ + protected SessionBindingListener(LinkedList applications) { + this.applications = applications; + } + + /** + * @see javax.servlet.http.HttpSessionBindingListener#valueBound(HttpSessionBindingEvent) + */ + public void valueBound(HttpSessionBindingEvent arg0) { + // We are not interested in bindings + } + + /** + * @see javax.servlet.http.HttpSessionBindingListener#valueUnbound(HttpSessionBindingEvent) + */ + public void valueUnbound(HttpSessionBindingEvent event) { + // If the binding listener is unbound from the session, the + // session must be closing + if (event.getName().equals(SESSION_BINDING_LISTENER)) { + // Close all applications related to given session + Object[] apps = applications.toArray(); + for (int i = 0; i < apps.length; i++) { + if (apps[i] != null) { + // Close application + ((Application) apps[i]).close(); + + synchronized (applicationToLastRequestDate) { + applicationToLastRequestDate.remove(apps[i]); + } + synchronized (applicationToAjaxAppMgrMap) { + applicationToAjaxAppMgrMap.remove(apps[i]); + } + // Remove application from applications list + applications.remove(apps[i]); + } + } + } + } + } + + /** + * Implementation of ParameterHandler.ErrorEvent interface. + */ + public class ParameterHandlerErrorImpl implements + ParameterHandler.ErrorEvent { + + private ParameterHandler owner; + + private Throwable throwable; + + /** + * + * @param owner + * @param throwable + */ + private ParameterHandlerErrorImpl(ParameterHandler owner, + Throwable throwable) { + this.owner = owner; + this.throwable = throwable; + } + + /** + * Gets the contained throwable. + * + * @see com.itmill.toolkit.terminal.Terminal.ErrorEvent#getThrowable() + */ + public Throwable getThrowable() { + return this.throwable; + } + + /** + * Gets the source ParameterHandler. + * + * @see com.itmill.toolkit.terminal.ParameterHandler.ErrorEvent#getParameterHandler() + */ + public ParameterHandler getParameterHandler() { + return this.owner; + } + + } + + /** + * 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; + } + + /** + * Gets the contained throwable. + * + * @see com.itmill.toolkit.terminal.Terminal.ErrorEvent#getThrowable() + */ + public Throwable getThrowable() { + return this.throwable; + } + + /** + * Gets the source URIHandler. + * + * @see com.itmill.toolkit.terminal.URIHandler.ErrorEvent#getURIHandler() + */ + public URIHandler getURIHandler() { + return this.owner; + } + } + + /** + * Gets AJAX application manager for an application. + * + * If this application has not been running in ajax mode before, new manager + * is created and web adapter stops listening to changes. + * + * @param application + * @return AJAX Application Manager + */ + private ApplicationManager getApplicationManager(Application application) { + ApplicationManager mgr = (ApplicationManager) applicationToAjaxAppMgrMap + .get(application); + + // This application is going from Web to AJAX mode, create new manager + if (mgr == null) { + // Creates new manager + mgr = new ApplicationManager(application, this); + applicationToAjaxAppMgrMap.put(application, mgr); + + // Manager takes control over the application + mgr.takeControl(); + } + + return mgr; + } + + /** + * Gets resource path using different implementations. Required fo + * supporting different servlet container implementations (application + * servers). + * + * @param servletContext + * @param path + * the resource path. + * @return the resource path. + */ + protected static String getResourcePath(ServletContext servletContext, + String path) { + String resultPath = null; + resultPath = servletContext.getRealPath(path); + if (resultPath != null) { + return resultPath; + } else { + try { + URL url = servletContext.getResource(path); + resultPath = url.getFile(); + } catch (Exception e) { + // ignored + } + } + return resultPath; + } + +} \ No newline at end of file diff --git a/src/com/itmill/toolkit/terminal/gwt/server/HttpUploadStream.java b/src/com/itmill/toolkit/terminal/gwt/server/HttpUploadStream.java new file mode 100644 index 0000000000..b92fb9d451 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/server/HttpUploadStream.java @@ -0,0 +1,114 @@ +/* ************************************************************************* + + 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.InputStream; + +/** + * WebAdapter implementation of the UploadStream interface. + * + * @author IT Mill Ltd. + * @version + * @VERSION@ + * @since 3.0 + */ +public class HttpUploadStream implements + com.itmill.toolkit.terminal.UploadStream { + + /** + * Holds value of property variableName. + */ + private String streamName; + + private String contentName; + + private String contentType; + + /** + * Holds value of property variableValue. + */ + private InputStream stream; + + /** + * Creates a new instance of UploadStreamImpl + * + * @param name + * the name of the stream. + * @param stream + * the input stream. + * @param contentName + * the name of the content. + * @param contentType + * the type of the content. + */ + public HttpUploadStream(String name, InputStream stream, + String contentName, String contentType) { + this.streamName = name; + this.stream = stream; + this.contentName = contentName; + this.contentType = contentType; + } + + /** + * Gets the name of the stream. + * + * @return the name of the stream. + */ + public String getStreamName() { + return this.streamName; + } + + /** + * Gets the input stream. + * + * @return the Input stream. + */ + public InputStream getStream() { + return this.stream; + } + + /** + * Gets the input stream content type. + * + * @return the content type of the input stream. + */ + public String getContentType() { + return this.contentType; + } + + /** + * Gets the stream content name. Stream content name usually differs from + * the actual stream name. It is used to identify the content of the stream. + * + * @return the Name of the stream content. + */ + public String getContentName() { + return this.contentName; + } +} diff --git a/src/com/itmill/toolkit/terminal/gwt/server/HttpVariableMap.java b/src/com/itmill/toolkit/terminal/gwt/server/HttpVariableMap.java new file mode 100644 index 0000000000..5fd4407971 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/server/HttpVariableMap.java @@ -0,0 +1,791 @@ +/* ************************************************************************* + + 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 com.itmill.toolkit.terminal.SystemError; +import com.itmill.toolkit.terminal.Terminal; +import com.itmill.toolkit.terminal.UploadStream; +import com.itmill.toolkit.terminal.VariableOwner; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.List; +import java.util.StringTokenizer; +import java.util.Enumeration; +import java.util.WeakHashMap; +import java.io.IOException; +import java.lang.ref.WeakReference; + +import javax.servlet.http.HttpServletRequest; +import java.util.LinkedList; +import java.util.Iterator; + +/** + * Class implementing the variable mappings. + * + * @author IT Mill Ltd. + * @version + * @VERSION@ + * @since 3.0 + */ +public class HttpVariableMap { + + // Id <-> (Owner,Name) mapping + private Map idToNameMap = new HashMap(); + + private Map idToTypeMap = new HashMap(); + + private Map idToOwnerMap = new HashMap(); + + private Map idToValueMap = new HashMap(); + + private Map ownerToNameToIdMap = new WeakHashMap(); + + private Object mapLock = new Object(); + + // Id generator + private long lastId = 0; + + /** + * Converts the string to a supported class. + * + * @param type + * @param value + * @throws java.lang.ClassCastException + */ + private static Object convert(Class type, String value) + throws java.lang.ClassCastException { + try { + + // Boolean typed variables + if (type.equals(Boolean.class)) + return new Boolean(!(value.equals("") || value.equals("false"))); + + // Integer typed variables + if (type.equals(Integer.class)) + return new Integer(value.trim()); + + // String typed variables + if (type.equals(String.class)) + return value; + + throw new ClassCastException("Unsupported type: " + type.getName()); + } catch (NumberFormatException e) { + return null; + } + } + + /** + * Registers a new variable. + * + * @param name + * the name of the variable. + * @param type + * @param value + * @param owner + * the Listener for variable changes. + * + * @return id to assigned for this variable. + */ + public String registerVariable(String name, Class type, Object value, + VariableOwner owner) { + + // Checks that the type of the class is supported + if (!(type.equals(Boolean.class) || type.equals(Integer.class) + || type.equals(String.class) || type.equals(String[].class) || type + .equals(UploadStream.class))) + throw new SystemError("Unsupported variable type: " + + type.getClass()); + + synchronized (mapLock) { + + // Checks if the variable is already mapped + HashMap nameToIdMap = (HashMap) ownerToNameToIdMap.get(owner); + if (nameToIdMap == null) { + nameToIdMap = new HashMap(); + ownerToNameToIdMap.put(owner, nameToIdMap); + } + String id = (String) nameToIdMap.get(name); + + if (id == null) { + // Generates new id and register it + id = "v" + String.valueOf(++lastId); + nameToIdMap.put(name, id); + idToOwnerMap.put(id, new WeakReference(owner)); + idToNameMap.put(id, name); + idToTypeMap.put(id, type); + } + + idToValueMap.put(id, value); + + return id; + } + } + + /** + * Unregisters the variable. + * + * @param name + * the name of the variable. + * @param owner + * the Listener for variable changes. + */ + public void unregisterVariable(String name, VariableOwner owner) { + + synchronized (mapLock) { + + // Gets the id + HashMap nameToIdMap = (HashMap) ownerToNameToIdMap.get(owner); + if (nameToIdMap == null) + return; + String id = (String) nameToIdMap.get(name); + if (id != null) + return; + + // Removes all the mappings + nameToIdMap.remove(name); + if (nameToIdMap.isEmpty()) + ownerToNameToIdMap.remove(owner); + idToNameMap.remove(id); + idToTypeMap.remove(id); + idToValueMap.remove(id); + idToOwnerMap.remove(id); + + } + } + + /** + * @author IT Mill Ltd. + * @version + * @VERSION@ + * @since 3.0 + */ + private class ParameterContainer { + + /** + * Constructs the mapping: listener to set of listened parameter names. + */ + private HashMap parameters = new HashMap(); + + /** + * Parameter values. + */ + private HashMap values = new HashMap(); + + /** + * Multipart parser used for parsing the request. + */ + private ServletMultipartRequest parser = null; + + /** + * Name - Value mapping of parameters that are not variables. + */ + private HashMap nonVariables = new HashMap(); + + /** + * Creates a new parameter container and parse the parameters from the + * request using GET, POST and POST/MULTIPART parsing. + * + * @param req + * the HTTP request. + * @throws IOException + * if the writing failed due to input/output error. + */ + public ParameterContainer(HttpServletRequest req) throws IOException { + // Parse GET / POST parameters + for (Enumeration e = req.getParameterNames(); e.hasMoreElements();) { + String paramName = (String) e.nextElement(); + String[] paramValues = req.getParameterValues(paramName); + addParam(paramName, paramValues); + } + + // Parse multipart variables + try { + parser = new ServletMultipartRequest(req, + MultipartRequest.MAX_READ_BYTES); + } catch (IllegalArgumentException ignored) { + parser = null; + } + + if (parser != null) { + for (Enumeration e = parser.getFileParameterNames(); e + .hasMoreElements();) { + String paramName = (String) e.nextElement(); + addParam(paramName, null); + } + for (Enumeration e = parser.getParameterNames(); e + .hasMoreElements();) { + String paramName = (String) e.nextElement(); + Enumeration val = parser.getURLParameters(paramName); + + // Create a linked list from enumeration to calculate + // elements + LinkedList l = new LinkedList(); + while (val.hasMoreElements()) + l.addLast(val.nextElement()); + + // String array event constructor + String[] s = new String[l.size()]; + Iterator i = l.iterator(); + for (int j = 0; j < s.length; j++) + s[j] = (String) i.next(); + + addParam(paramName, s); + } + } + + } + + /** + * Adds the parameter to container. + * + * @param name + * the name of the parameter. + * @param value + */ + private void addParam(String name, String[] value) { + + // Support name="set:name=value" value="ignored" notation + if (name.startsWith("set:")) { + int equalsIndex = name.indexOf('='); + value[0] = name.substring(equalsIndex + 1, name.length()); + name = name.substring(4, equalsIndex); + String[] curVal = (String[]) values.get(name); + if (curVal != null) { + String[] newVal = new String[1 + curVal.length]; + newVal[curVal.length] = value[0]; + for (int i = 0; i < curVal.length; i++) + newVal[i] = curVal[i]; + value = newVal; + + // Special case - if the set:-method is used for + // declaring array of length 2, where either of the + // following conditions are true: + // - the both items are the same + // - the both items have the same length and + // - the items only differ on last character + // - second last character is '.' + // - last char of one string is 'x' and other is 'y' + // Browser is unporposely modifying the name. + if (value.length == 2 + && value[0].length() == value[1].length()) { + boolean same = true; + for (int i = 0; i < value[0].length() - 1 && same; i++) + if (value[0].charAt(i) != value[1].charAt(i)) + same = false; + if (same + && ((value[0].charAt(value[0].length() - 1) == 'x' && value[1] + .charAt(value[1].length() - 1) == 'y') || (value[0] + .charAt(value[0].length() - 1) == 'y' && value[1] + .charAt(value[1].length() - 1) == 'x'))) { + value = new String[] { value[0].substring(0, + value[1].length() - 2) }; + } else if (same && value[0].equals(value[1])) + value = new String[] { value[0] }; + } + + // Special case - if the set:-method is used for + // declaring array of length 3, where all of the + // following conditions are true: + // - two last items have the same length + // - the first item is 2 chars shorter + // - the longer items only differ on last character + // - the shortest item is a prefix of the longer ones + // - second last character of longer ones is '.' + // - last char of one long string is 'x' and other is 'y' + // Browser is unporposely modifying the name. (Mozilla, + // Firefox, ..) + if (value.length == 3 + && value[1].length() == value[2].length() + && value[0].length() + 2 == value[1].length()) { + boolean same = true; + for (int i = 0; i < value[1].length() - 1 && same; i++) + if (value[2].charAt(i) != value[1].charAt(i)) + same = false; + for (int i = 0; i < value[0].length() && same; i++) + if (value[0].charAt(i) != value[1].charAt(i)) + same = false; + if (same + && (value[2].charAt(value[2].length() - 1) == 'x' && value[1] + .charAt(value[1].length() - 1) == 'y') + || (value[2].charAt(value[2].length() - 1) == 'y' && value[1] + .charAt(value[1].length() - 1) == 'x')) { + value = new String[] { value[0] }; + } + } + + } + } + + // Support for setting arrays in format + // set-array:name=value1,value2,value3,... + else if (name.startsWith("set-array:")) { + int equalsIndex = name.indexOf('='); + if (equalsIndex < 0) + return; + + StringTokenizer commalist = new StringTokenizer(name + .substring(equalsIndex + 1), ","); + name = name.substring(10, equalsIndex); + String[] curVal = (String[]) values.get(name); + ArrayList elems = new ArrayList(); + + // Adds old values if present. + if (curVal != null) { + for (int i = 0; i < curVal.length; i++) + elems.add(curVal[i]); + } + while (commalist.hasMoreTokens()) { + String token = commalist.nextToken(); + if (token != null && token.length() > 0) + elems.add(token); + } + value = new String[elems.size()]; + for (int i = 0; i < value.length; i++) + value[i] = (String) elems.get(i); + + } + + // Support name="array:name" value="val1,val2,val3" notation + // All the empty elements are ignored + else if (name.startsWith("array:")) { + + name = name.substring(6); + StringTokenizer commalist = new StringTokenizer(value[0], ","); + String[] curVal = (String[]) values.get(name); + ArrayList elems = new ArrayList(); + + // Adds old values if present. + if (curVal != null) { + for (int i = 0; i < curVal.length; i++) + elems.add(curVal[i]); + } + while (commalist.hasMoreTokens()) { + String token = commalist.nextToken(); + if (token != null && token.length() > 0) + elems.add(token); + } + value = new String[elems.size()]; + for (int i = 0; i < value.length; i++) + value[i] = (String) elems.get(i); + } + + // Support declaring variables with name="declare:name" + else if (name.startsWith("declare:")) { + name = name.substring(8); + value = (String[]) values.get(name); + if (value == null) + value = new String[0]; + } + + // Gets the owner + WeakReference ref = (WeakReference) idToOwnerMap.get(name); + VariableOwner owner = null; + if (ref != null) + owner = (VariableOwner) ref.get(); + + // Adds the parameter to mapping only if they have owners + if (owner != null) { + Set p = (Set) parameters.get(owner); + if (p == null) + parameters.put(owner, p = new HashSet()); + p.add(name); + if (value != null) + values.put(name, value); + } + + // If the owner can not be found + else { + + // If parameter has been mapped before, remove the old owner + // mapping + if (ref != null) { + + // The owner has been destroyed, so we remove the mappings + idToNameMap.remove(name); + idToOwnerMap.remove(name); + idToTypeMap.remove(name); + idToValueMap.remove(name); + } + + // Adds the parameter to set of non-variables + nonVariables.put(name, value); + } + + } + + /** + * Gets the set of all parameters connected to given variable owner. + * + * @param owner + * the Listener for variable changes. + * @return the set of all parameters connected to variable owner. + */ + public Set getParameters(VariableOwner owner) { + if (owner == null) + return null; + return (Set) parameters.get(owner); + } + + /** + * Gets the set of all variable owners owning parameters in this + * request. + * + * @return + */ + public Set getOwners() { + return parameters.keySet(); + } + + /** + * Gets the value of a parameter. + * + * @param parameterName + * the name of the parameter. + * @return the value of the parameter. + */ + public String[] getValue(String parameterName) { + return (String[]) values.get(parameterName); + } + + /** + * Gets the servlet multipart parser. + * + * @return the parser. + */ + public ServletMultipartRequest getParser() { + return parser; + } + + /** + * Gets the name - value[] mapping of non variable paramteres. + * + * @return + */ + public Map getNonVariables() { + return nonVariables; + } + } + + /** + * Handles all variable changes in this request. + * + * @param req + * the Http request to handle. + * @param errorListener + * If the list is non null, only the listed listeners are served. + * Otherwise all the listeners are served. + * @return Name to Value[] mapping of unhandled variables. + * @throws IOException + * if the writing failed due to input/output error. + */ + public Map handleVariables(HttpServletRequest req, + Terminal.ErrorListener errorListener) throws IOException { + + // Gets the parameters + ParameterContainer parcon = new ParameterContainer(req); + + // Sorts listeners to dependency order + List listeners = getDependencySortedListenerList(parcon.getOwners()); + + // Handles all parameters for all listeners + while (!listeners.isEmpty()) { + VariableOwner listener = (VariableOwner) listeners.remove(0); + boolean changed = false; // Has any of this owners variabes + // changed + // Handle all parameters for listener + Set params = parcon.getParameters(listener); + if (params != null) { // Name value mapping + Map variables = new HashMap(); + for (Iterator pi = params.iterator(); pi.hasNext();) { + // Gets the name of the parameter + String param = (String) pi.next(); + // Extracts more information about the parameter + String varName = (String) idToNameMap.get(param); + Class varType = (Class) idToTypeMap.get(param); + Object varOldValue = idToValueMap.get(param); + if (varName == null || varType == null) + Log + .warn("VariableMap: No variable found for parameter " + + param + + " (" + + varName + + "," + + listener + ")"); + else { + + ServletMultipartRequest parser = parcon.getParser(); + + // Upload events + if (varType.equals(UploadStream.class)) { + if (parser != null + && parser.getFileParameter(param, + MultipartRequest.FILENAME) != null) { + String filename = (String) parser + .getFileParameter(param, + MultipartRequest.FILENAME); + String contentType = (String) parser + .getFileParameter(param, + MultipartRequest.CONTENT_TYPE); + UploadStream upload = new HttpUploadStream( + varName, parser.getFileContents(param), + filename, contentType); + variables.put(varName, upload); + changed = true; + } + } + + // Normal variable change events + else { + // First try to parse the event without multipart + String[] values = parcon.getValue(param); + if (values != null) { + + if (varType.equals(String[].class)) { + variables.put(varName, values); + changed |= (!Arrays.equals(values, + (String[]) varOldValue)); + } else { + try { + if (values.length == 1) { + Object val = convert(varType, + values[0]); + variables.put(varName, val); + changed |= ((val == null && varOldValue != null) || (val != null && !val + .equals(varOldValue))); + } else if (values.length == 0 + && varType + .equals(Boolean.class)) { + Object val = new Boolean(false); + variables.put(varName, val); + changed |= (!val + .equals(varOldValue)); + } else { + Log.warn("Empty variable '" + + varName + "' of type " + + varType.toString()); + } + + } catch (java.lang.ClassCastException e) { + Log + .except( + "WebVariableMap conversion exception", + e); + errorListener + .terminalError(new TerminalErrorImpl( + e)); + } + } + } + } + } + } + + // Do the valuechange if the listener is enabled + if (listener.isEnabled() && changed) { + try { + listener.changeVariables(req, variables); + } catch (Throwable t) { + // Notify the error listener + errorListener.terminalError(new VariableOwnerErrorImpl( + listener, t)); + } + } + } + } + + return parcon.getNonVariables(); + } + + /** + * Implementation of VariableOwner.Error interface. + */ + public class TerminalErrorImpl implements Terminal.ErrorEvent { + private Throwable throwable; + + /** + * + * @param throwable + */ + private TerminalErrorImpl(Throwable throwable) { + this.throwable = throwable; + } + + /** + * Gets the contained throwable. + * + * @see com.itmill.toolkit.terminal.Terminal.ErrorEvent#getThrowable() + */ + public Throwable getThrowable() { + return this.throwable; + } + + } + + /** + * Implementation of VariableOwner.Error interface. + */ + public class VariableOwnerErrorImpl extends TerminalErrorImpl implements + VariableOwner.ErrorEvent { + + private VariableOwner owner; + + /** + * + * @param owner + * the Listener for variable changes. + * @param throwable + */ + private VariableOwnerErrorImpl(VariableOwner owner, Throwable throwable) { + super(throwable); + this.owner = owner; + } + + /** + * Gets the source VariableOwner. + * + * @see com.itmill.toolkit.terminal.VariableOwner.ErrorEvent#getVariableOwner() + */ + public VariableOwner getVariableOwner() { + return this.owner; + } + + } + + /** + * Resolves the VariableOwners needed from the request and sort them to + * assure that the dependencies are met (as well as possible). + * + * @param listeners + * @return List of variable list changers, that are needed for handling all + * the variables in the request + */ + private List getDependencySortedListenerList(Set listeners) { + + LinkedList resultNormal = new LinkedList(); + LinkedList resultImmediate = new LinkedList(); + + // Go trough the listeners and either add them to result or resolve + // their dependencies + HashMap deepdeps = new HashMap(); + LinkedList unresolved = new LinkedList(); + for (Iterator li = listeners.iterator(); li.hasNext();) { + + VariableOwner listener = (VariableOwner) li.next(); + if (listener != null) { + Set dependencies = listener.getDirectDependencies(); + + // The listeners with no dependencies are added to the front of + // the + // list directly + if (dependencies == null || dependencies.isEmpty()) { + if (listener.isImmediate()) + resultImmediate.addFirst(listener); + else + resultNormal.addFirst(listener); + } + + // Resolve deep dependencies for the listeners with dependencies + // (the listeners will be added to the end of results in correct + // dependency order later). Also the dependencies of all the + // depended listeners are resolved. + else if (deepdeps.get(listener) == null) { + + // Set the fifo for unresolved parents to contain only the + // listener to be resolved + unresolved.clear(); + unresolved.add(listener); + + // Resolve dependencies + HashSet tmpdeepdeps = new HashSet(); + while (!unresolved.isEmpty()) { + + VariableOwner l = (VariableOwner) unresolved + .removeFirst(); + if (!tmpdeepdeps.contains(l)) { + tmpdeepdeps.add(l); + if (deepdeps.containsKey(l)) { + tmpdeepdeps.addAll((Set) deepdeps.get(l)); + } else { + Set deps = l.getDirectDependencies(); + if (deps != null && !deps.isEmpty()) + for (Iterator di = deps.iterator(); di + .hasNext();) { + Object d = di.next(); + if (d != null + && !tmpdeepdeps.contains(d)) + unresolved.addLast(d); + } + } + } + } + + tmpdeepdeps.remove(listener); + deepdeps.put(listener, tmpdeepdeps); + } + } + } + + // Adds the listeners with dependencies in sane order to the result + for (Iterator li = deepdeps.keySet().iterator(); li.hasNext();) { + VariableOwner l = (VariableOwner) li.next(); + boolean immediate = l.isImmediate(); + + // Adds each listener after the last depended listener already in + // the list + int index = -1; + for (Iterator di = ((Set) deepdeps.get(l)).iterator(); di.hasNext();) { + int k; + Object depended = di.next(); + if (immediate) { + k = resultImmediate.lastIndexOf(depended); + } else { + k = resultNormal.lastIndexOf(depended); + } + if (k > index) + index = k; + } + if (immediate) { + resultImmediate.add(index + 1, l); + } else { + resultNormal.add(index + 1, l); + } + } + + // Appends immediate listeners to normal listeners + // This way the normal handlers are always called before + // immediate ones + resultNormal.addAll(resultImmediate); + return resultNormal; + } +} diff --git a/src/com/itmill/toolkit/terminal/gwt/server/JSONPaintTarget.java b/src/com/itmill/toolkit/terminal/gwt/server/JSONPaintTarget.java deleted file mode 100644 index a1a62918e9..0000000000 --- a/src/com/itmill/toolkit/terminal/gwt/server/JSONPaintTarget.java +++ /dev/null @@ -1,165 +0,0 @@ -package com.itmill.toolkit.terminal.gwt.server; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Stack; - -import com.itmill.toolkit.Application; -import com.itmill.toolkit.terminal.ApplicationResource; -import com.itmill.toolkit.terminal.ExternalResource; -import com.itmill.toolkit.terminal.PaintException; -import com.itmill.toolkit.terminal.PaintTarget; -import com.itmill.toolkit.terminal.Paintable; -import com.itmill.toolkit.terminal.Resource; -import com.itmill.toolkit.terminal.ThemeResource; -import com.itmill.toolkit.terminal.VariableOwner; - -public class JSONPaintTarget implements PaintTarget { - - Stack tags = new Stack(); - Tag tag = null; - - public void addAttribute(String name, boolean value) throws PaintException { - tag.attrs.put(name, new Boolean(value)); - } - - public void addAttribute(String name, int value) throws PaintException { - tag.attrs.put(name, new Integer(value)); - } - - public void addAttribute(String name, Resource value) throws PaintException { - if (value instanceof ExternalResource) { - addAttribute(name, ((ExternalResource) value).getURL()); - - } else if (value instanceof ApplicationResource) { - ApplicationResource r = (ApplicationResource) value; - Application a = r.getApplication(); - if (a == null) - throw new PaintException( - "Application not specified for resorce " - + value.getClass().getName()); - String uri = a.getURL().getPath(); - if (uri.charAt(uri.length() - 1) != '/') - uri += "/"; - uri += a.getRelativeLocation(r); - addAttribute(name, uri); - - } else if (value instanceof ThemeResource) { - String uri = "theme://" + ((ThemeResource) value).getResourceId(); - addAttribute(name, uri); - } else - throw new PaintException("Ajax adapter does not " - + "support resources of type: " - + value.getClass().getName()); - } - - public void addAttribute(String name, long value) throws PaintException { - tag.attrs.put(name, new Long(value)); - } - - public void addAttribute(String name, String value) throws PaintException { - tag.attrs.put(name, value); - } - - public void addCharacterData(String text) throws PaintException { - if (tag.data != null || tag.xml != null || tag.children.size() > 0) - throw new IllegalStateException("Character data can not be combined with XML or child-nodes"); - tag.data = text; - } - - public void addSection(String sectionTagName, String sectionData) - throws PaintException { - Tag t = new Tag(sectionTagName); - t.data = sectionData; - if (tag.data != null || tag.xml != null) - throw new IllegalStateException("Children can not be combined with XML or chardata"); - tag.children.add(t); - } - - public void addText(String text) throws PaintException { - if (tag.data != null || tag.xml != null || tag.children.size() > 0) - throw new IllegalStateException("Text-data can not be combined with XML or child-nodes"); - tag.data = text; - } - - public void addUIDL(String uidl) throws PaintException { - if (tag.data != null || tag.xml != null || tag.children.size() > 0) - throw new IllegalStateException("XML can not be combined with text or child-nodes"); - tag.xml = uidl; - } - - public void addUploadStreamVariable(VariableOwner owner, String name) - throws PaintException { - // TODO Auto-generated method stub - - } - - public void addVariable(VariableOwner owner, String name, String value) - throws PaintException { - // TODO Auto-generated method stub - - } - - public void addVariable(VariableOwner owner, String name, int value) - throws PaintException { - // TODO Auto-generated method stub - - } - - public void addVariable(VariableOwner owner, String name, boolean value) - throws PaintException { - // TODO Auto-generated method stub - - } - - public void addVariable(VariableOwner owner, String name, String[] value) - throws PaintException { - // TODO Auto-generated method stub - - } - - public void addXMLSection(String sectionTagName, String sectionData, - String namespace) throws PaintException { - // TODO Auto-generated method stub - - } - - public void endTag(String tagName) throws PaintException { - - } - - public boolean startTag(Paintable paintable, String tag) - throws PaintException { - // TODO Auto-generated method stub - return false; - } - - public void startTag(String tagName) throws PaintException { - // TODO Auto-generated method stub - - } - - private class Tag { - HashMap attrs = new HashMap(); - - ArrayList children = new ArrayList(); - - ArrayList vars = new ArrayList(); - - String xml = null; - - String data = null; - - String tag; - - Tag(String tag) { - this.tag = tag; - } - } - - public void addAttribute(String string, Object[] keys) { - // TODO Auto-generated method stub - - } - -} diff --git a/src/com/itmill/toolkit/terminal/gwt/server/JsonPaintTarget.java b/src/com/itmill/toolkit/terminal/gwt/server/JsonPaintTarget.java new file mode 100644 index 0000000000..6e4662e557 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/server/JsonPaintTarget.java @@ -0,0 +1,980 @@ +/* ************************************************************************* + + 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 com.itmill.toolkit.Application; +import com.itmill.toolkit.terminal.ApplicationResource; +import com.itmill.toolkit.terminal.ExternalResource; +import com.itmill.toolkit.terminal.PaintException; +import com.itmill.toolkit.terminal.PaintTarget; +import com.itmill.toolkit.terminal.Paintable; +import com.itmill.toolkit.terminal.Resource; +import com.itmill.toolkit.terminal.ThemeResource; +import com.itmill.toolkit.terminal.UploadStream; +import com.itmill.toolkit.terminal.VariableOwner; + +import java.io.BufferedWriter; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Set; +import java.util.Stack; +import java.util.Vector; + +/** + * User Interface Description Language Target. + * + * @author IT Mill Ltd. + * @version + * @VERSION@ + * @since 5.0 + */ +public class JsonPaintTarget implements PaintTarget { + + /* Document type declarations */ + + private final static String UIDL_ARG_NAME = "name"; + + private final static String UIDL_ARG_VALUE = "value"; + + private final static String UIDL_ARG_ID = "id"; + + private Stack mOpenTags; + + private Stack openJsonTags; + + private boolean mTagArgumentListOpen; + + private PrintWriter uidlBuffer; + + private AjaxVariableMap variableMap; + + private boolean closed = false; + + private ApplicationManager manager; + + private boolean trackPaints = false; + + private int numberOfPaints = 0; + + private int changes = 0; + + Set preCachedResources = new HashSet(); + + private boolean customLayoutArgumentsOpen = false; + + private JsonTag tag; + + /** + * Creates a new XMLPrintWriter, without automatic line flushing. + * + * @param variableMap + * @param manager + * @param outWriter + * A character-output stream. + * @throws PaintException + * if the paint operation failed. + */ + public JsonPaintTarget(AjaxVariableMap variableMap, + ApplicationManager manager, PrintWriter outWriter) + throws PaintException { + + this.manager = manager; + // Sets the variable map + this.variableMap = variableMap; + + // Sets the target for UIDL writing + this.uidlBuffer = outWriter; + + // Initialize tag-writing + mOpenTags = new Stack(); + openJsonTags = new Stack(); + mTagArgumentListOpen = false; + + // Adds document declaration + + // Adds UIDL start tag and its attributes + } + + public void startTag(String tagName) throws PaintException { + startTag(tagName, false); + } + + /** + * Prints the element start tag. + * + *
+	 *   Todo:
+	 *    Checking of input values
+	 *    
+	 * 
+ * + * @param tagName + * the name of the start tag. + * @throws PaintException + * if the paint operation failed. + * + */ + public void startTag(String tagName, boolean isChildNode) + throws PaintException { + // In case of null data output nothing: + if (tagName == null) + throw new NullPointerException(); + + // Increments paint tracker + if (this.isTrackPaints()) { + this.numberOfPaints++; + } + + // Ensures that the target is open + if (this.closed) + throw new PaintException( + "Attempted to write to a closed PaintTarget."); + + if (tag != null) { + openJsonTags.push(tag); + } + // Checks tagName and attributes here + mOpenTags.push(tagName); + + tag = new JsonTag(tagName); + + mTagArgumentListOpen = true; + + customLayoutArgumentsOpen = "customlayout".equals(tagName); + } + + /** + * Prints the element end tag. + * + * If the parent tag is closed before every child tag is closed an + * PaintException is raised. + * + * @param tag + * the name of the end tag. + * @throws Paintexception + * if the paint operation failed. + */ + public void endTag(String tagName) throws PaintException { + // In case of null data output nothing: + if (tagName == null) + throw new NullPointerException(); + + // Ensure that the target is open + if (this.closed) + throw new PaintException( + "Attempted to write to a closed PaintTarget."); + + if (openJsonTags.size() > 0) { + JsonTag parent = (JsonTag) openJsonTags.pop(); + + String lastTag = ""; + + lastTag = (String) mOpenTags.pop(); + if (!tagName.equalsIgnoreCase(lastTag)) + throw new PaintException("Invalid UIDL: wrong ending tag: '" + + tagName + "' expected: '" + lastTag + "'."); + + parent.addData(tag.getJSON()); + + tag = parent; + } else { + changes++; + this.uidlBuffer.print(((changes > 1) ? "," : "") + tag.getJSON()); + tag = null; + } + } + + /** + * Substitutes the XML sensitive characters with predefined XML entities. + * + * @param xml + * the String to be substituted. + * @return A new string instance where all occurrences of XML sensitive + * characters are substituted with entities. + */ + static public String escapeXML(String xml) { + if (xml == null || xml.length() <= 0) + return ""; + return escapeXML(new StringBuffer(xml)).toString(); + } + + /** + * Substitutes the XML sensitive characters with predefined XML entities. + * + * @param xml + * the String to be substituted. + * @return A new StringBuffer instance where all occurrences of XML + * sensitive characters are substituted with entities. + * + */ + static public StringBuffer escapeXML(StringBuffer xml) { + if (xml == null || xml.length() <= 0) + return new StringBuffer(""); + + StringBuffer result = new StringBuffer(xml.length() * 2); + + for (int i = 0; i < xml.length(); i++) { + char c = xml.charAt(i); + String s = toXmlChar(c); + if (s != null) { + result.append(s); + } else { + result.append(c); + } + } + return result; + } + + static public String escapeJSON(String s) { + if (s == null) + return ""; + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < s.length(); i++) { + char ch = s.charAt(i); + switch (ch) { + case '"': + sb.append("\\\""); + break; + case '\\': + sb.append("\\\\"); + break; + case '\b': + sb.append("\\b"); + break; + case '\f': + sb.append("\\f"); + break; + case '\n': + sb.append("\\n"); + break; + case '\r': + sb.append("\\r"); + break; + case '\t': + sb.append("\\t"); + break; + case '/': + sb.append("\\/"); + break; + default: + if (ch >= '\u0000' && ch <= '\u001F') { + String ss = Integer.toHexString(ch); + sb.append("\\u"); + for (int k = 0; k < 4 - ss.length(); k++) { + sb.append('0'); + } + sb.append(ss.toUpperCase()); + } else { + sb.append(ch); + } + } + } + return sb.toString(); + } + + /** + * Substitutes a XML sensitive character with predefined XML entity. + * + * @param c + * the Character to be replaced with an entity. + * @return String of the entity or null if character is not to be replaced + * with an entity. + */ + private static String toXmlChar(char c) { + switch (c) { + case '&': + return "&"; // & => & + case '>': + return ">"; // > => > + case '<': + return "<"; // < => < + case '"': + return """; // " => " + case '\'': + return "'"; // ' => ' + default: + return null; + } + } + + /** + * Prints XML-escaped text. + * + * @param str + * @throws PaintException + * if the paint operation failed. + * + */ + public void addText(String str) throws PaintException { + tag.addData("\"" + escapeJSON(str) + "\""); + } + + /** + * Adds a boolean attribute to component. Atributes must be added before any + * content is written. + * + * @param name + * the Attribute name. + * @param value + * the Attribute value. + * @throws PaintException + * if the paint operation failed. + */ + public void addAttribute(String name, boolean value) throws PaintException { + tag.addAttribute("\"" + name + "\":" + (value ? "true" : "false")); + } + + /** + * Adds a resource attribute to component. Atributes must be added before + * any content is written. + * + * @param name + * the Attribute name. + * @param value + * the Attribute value. + * + * @throws PaintException + * if the paint operation failed. + */ + public void addAttribute(String name, Resource value) throws PaintException { + + if (value instanceof ExternalResource) { + addAttribute(name, ((ExternalResource) value).getURL()); + + } else if (value instanceof ApplicationResource) { + ApplicationResource r = (ApplicationResource) value; + Application a = r.getApplication(); + if (a == null) + throw new PaintException( + "Application not specified for resorce " + + value.getClass().getName()); + String uri = a.getURL().getPath(); + if (uri.charAt(uri.length() - 1) != '/') + uri += "/"; + uri += a.getRelativeLocation(r); + addAttribute(name, uri); + + } else if (value instanceof ThemeResource) { + String uri = "theme://" + ((ThemeResource) value).getResourceId(); + addAttribute(name, uri); + } else + throw new PaintException("Ajax adapter does not " + + "support resources of type: " + + value.getClass().getName()); + + } + + /** + * Adds a integer attribute to component. Atributes must be added before any + * content is written. + * + * @param name + * the Attribute name. + * @param value + * the Attribute value. + * + * @throws PaintException + * if the paint operation failed. + */ + public void addAttribute(String name, int value) throws PaintException { + tag.addAttribute("\"" + name + "\":" + String.valueOf(value)); + } + + /** + * Adds a long attribute to component. Atributes must be added before any + * content is written. + * + * @param name + * the Attribute name. + * @param value + * the Attribute value. + * + * @throws PaintException + * if the paint operation failed. + */ + public void addAttribute(String name, long value) throws PaintException { + tag.addAttribute("\"" + name + "\":" + String.valueOf(value)); + } + + /** + * Adds a string attribute to component. Atributes must be added before any + * content is written. + * + * @param name + * the String attribute name. + * @param value + * the String attribute value. + * + * @throws PaintException + * if the paint operation failed. + */ + public void addAttribute(String name, String value) throws PaintException { + // In case of null data output nothing: + if ((value == null) || (name == null)) + throw new NullPointerException( + "Parameters must be non-null strings"); + + tag.addAttribute("\"" + name + "\": \"" + escapeJSON(value) + "\""); + + if (customLayoutArgumentsOpen && "style".equals(name)) + getPreCachedResources().add("layout/" + value + ".html"); + + if(name.equals("locale")) + manager.requireLocale(value); + + } + + public void addAttribute(String name, Object[] values) { + // In case of null data output nothing: + if ((values == null) || (name == null)) + throw new NullPointerException( + "Parameters must be non-null strings"); + StringBuffer buf = new StringBuffer(); + buf.append("\"" + name + "\":["); + for (int i = 0; i < values.length; i++) { + if (i > 0) + buf.append(","); + buf.append("\""); + buf.append(escapeJSON(values[i].toString())); + buf.append("\""); + } + buf.append("]"); + tag.addAttribute(buf.toString()); + } + + /** + * Adds a string type variable. + * + * @param owner + * the Listener for variable changes. + * @param name + * the Variable name. + * @param value + * the Variable initial value. + * + * @throws PaintException + * if the paint operation failed. + */ + public void addVariable(VariableOwner owner, String name, String value) + throws PaintException { + tag.addVariable(new StringVariable(owner, name, value)); + } + + /** + * Adds a int type variable. + * + * @param owner + * the Listener for variable changes. + * @param name + * the Variable name. + * @param value + * the Variable initial value. + * + * @throws PaintException + * if the paint operation failed. + */ + public void addVariable(VariableOwner owner, String name, int value) + throws PaintException { + tag.addVariable(new IntVariable(owner, name, value)); + } + + /** + * Adds a boolean type variable. + * + * @param owner + * the Listener for variable changes. + * @param name + * the Variable name. + * @param value + * the Variable initial value. + * + * @throws PaintException + * if the paint operation failed. + */ + public void addVariable(VariableOwner owner, String name, boolean value) + throws PaintException { + tag.addVariable(new BooleanVariable(owner, name, value)); + } + + /** + * Adds a string array type variable. + * + * @param owner + * the Listener for variable changes. + * @param name + * the Variable name. + * @param value + * the Variable initial value. + * + * @throws PaintException + * if the paint operation failed. + */ + public void addVariable(VariableOwner owner, String name, String[] value) + throws PaintException { + tag.addVariable(new ArrayVariable(owner, name, value)); + } + + /** + * Adds a upload stream type variable. + * + * TODO not converted for JSON + * + * @param owner + * the Listener for variable changes. + * @param name + * the Variable name. + * + * @throws PaintException + * if the paint operation failed. + */ + public void addUploadStreamVariable(VariableOwner owner, String name) + throws PaintException { + String code = variableMap.registerVariable(name, UploadStream.class, + null, owner); + startTag("uploadstream"); + addAttribute(UIDL_ARG_ID, code); + addAttribute(UIDL_ARG_NAME, name); + endTag("uploadstream"); + } + + /** + * Prints the single text section. + * + * Prints full text section. The section data is escaped + * + * @param sectionTagName + * the name of the tag. + * @param sectionData + * the section data to be printed. + * @throws PaintException + * if the paint operation failed. + */ + public void addSection(String sectionTagName, String sectionData) + throws PaintException { + tag.addData("{\"" + sectionTagName + "\":\"" + escapeJSON(sectionData) + + "\"}"); + } + + /** + * Adds XML directly to UIDL. + * + * @param xml + * the Xml to be added. + * @throws PaintException + * if the paint operation failed. + */ + public void addUIDL(String xml) throws PaintException { + + // Ensure that the target is open + if (this.closed) + throw new PaintException( + "Attempted to write to a closed PaintTarget."); + + // Make sure that the open start tag is closed before + // anything is written. + + // Escape and write what was given + if (xml != null) + tag.addData("\"" + escapeJSON(xml) + "\""); + + } + + /** + * Adds XML section with namespace. + * + * @param sectionTagName + * the name of the tag. + * @param sectionData + * the section data. + * @param namespace + * the namespace to be added. + * @throws PaintException + * if the paint operation failed. + * + * @see com.itmill.toolkit.terminal.PaintTarget#addXMLSection(String, + * String, String) + */ + public void addXMLSection(String sectionTagName, String sectionData, + String namespace) throws PaintException { + + // Ensure that the target is open + if (this.closed) + throw new PaintException( + "Attempted to write to a closed PaintTarget."); + + startTag(sectionTagName); + if (namespace != null) + addAttribute("xmlns", namespace); + mTagArgumentListOpen = false; + customLayoutArgumentsOpen = false; + + if (sectionData != null) + tag.addData("\"" + escapeJSON(sectionData) + "\""); + endTag(sectionTagName); + } + + /** + * Gets the UIDL already printed to stream. Paint target must be closed + * before the getUIDL can be called. + * + * @return the UIDL. + */ + public String getUIDL() { + if (this.closed) { + return uidlBuffer.toString(); + } + throw new IllegalStateException( + "Tried to read UIDL from open PaintTarget"); + } + + /** + * Closes the paint target. Paint target must be closed before the + * getUIDL can be called. Subsequent attempts to write to + * paint target. If the target was already closed, call to this function is + * ignored. will generate an exception. + * + * @throws PaintException + * if the paint operation failed. + */ + public void close() throws PaintException { + if (tag != null) + uidlBuffer.append(tag.getJSON()); + flush(); + this.closed = true; + } + + /** + * Method flush. + */ + private void flush() { + this.uidlBuffer.flush(); + } + + /** + * @see com.itmill.toolkit.terminal.PaintTarget#startTag(com.itmill.toolkit.terminal.Paintable, + * java.lang.String) + */ + public boolean startTag(Paintable paintable, String tagName) + throws PaintException { + startTag(tagName, true); + String id = manager.getPaintableId(paintable); + paintable.addListener(manager); + addAttribute("id", id); + return false; + } + + /** + * @see com.itmill.toolkit.terminal.PaintTarget#addCharacterData(java.lang.String) + */ + public void addCharacterData(String text) throws PaintException { + if (text != null) + tag.addData(text); + } + + /** + * + * @return + */ + public boolean isTrackPaints() { + return trackPaints; + } + + /** + * Gets the number of paints. + * + * @return the number of paints. + */ + public int getNumberOfPaints() { + return numberOfPaints; + } + + /** + * Sets the tracking to true or false. + * + * This also resets the number of paints. + * + * @param enabled + * is the tracking is enabled or not. + * @see #getNumberOfPaints() + */ + public void setTrackPaints(boolean enabled) { + this.trackPaints = enabled; + this.numberOfPaints = 0; + } + + /** + * This is basically a container for UI components variables, that will be + * added at the end of JSON object. + * + * @author mattitahvonen + * + */ + class JsonTag { + boolean firstField = false; + + Vector variables = new Vector(); + + Vector children = new Vector(); + + Vector attr = new Vector(); + + private HashMap childTagCounters = new HashMap(); + + StringBuffer data = new StringBuffer(); + + public boolean childrenArrayOpen = false; + + private boolean childNode = false; + + private boolean tagClosed = false; + + public JsonTag(String tagName) { + data.append("[\"" + tagName + "\""); + } + + private void closeTag() { + if (!tagClosed) { + data.append(attributesAsJsonObject()); + data.append(getData()); + // Writes the end (closing) tag + data.append("]"); + this.tagClosed = true; + } + } + + public String getJSON() { + if (!tagClosed) { + this.closeTag(); + } + return data.toString(); + } + + public void openChildrenArray() { + if (!childrenArrayOpen) { + // append("c : ["); + childrenArrayOpen = true; + // firstField = true; + } + } + + public void closeChildrenArray() { + // append("]"); + // firstField = false; + } + + public void setChildNode(boolean b) { + this.childNode = b; + } + + public boolean isChildNode() { + return childNode; + } + + public String startField() { + if (firstField) { + firstField = false; + return ""; + } else { + return ","; + } + } + + /** + * + * @param s + * json string, object or array + */ + public void addData(String s) { + children.add(s); + } + + public String getData() { + StringBuffer buf = new StringBuffer(); + Iterator it = children.iterator(); + while (it.hasNext()) { + buf.append(startField()); + buf.append(it.next()); + } + return buf.toString(); + } + + public void addAttribute(String jsonNode) { + attr.add(jsonNode); + } + + private String attributesAsJsonObject() { + StringBuffer buf = new StringBuffer(); + buf.append(startField()); + buf.append("{"); + for (Iterator iter = attr.iterator(); iter.hasNext();) { + String element = (String) iter.next(); + buf.append(element); + if (iter.hasNext()) + buf.append(","); + } + buf.append(tag.variablesAsJsonObject()); + buf.append("}"); + return buf.toString(); + } + + public void addVariable(Variable v) { + variables.add(v); + } + + private String variablesAsJsonObject() { + if (variables.size() == 0) + return ""; + StringBuffer buf = new StringBuffer(); + buf.append(startField()); + buf.append("\"v\":{"); + Iterator iter = variables.iterator(); + while (iter.hasNext()) { + Variable element = (Variable) iter.next(); + buf.append(element.getJsonPresentation()); + if (iter.hasNext()) + buf.append(","); + } + buf.append("}"); + return buf.toString(); + } + + class TagCounter { + int count; + + public TagCounter() { + count = 0; + } + + public void increment() { + count++; + } + + public String postfix(String s) { + if (count > 0) { + return s + count; + } + return s; + } + } + } + + abstract class Variable { + String code; + + String name; + + public abstract String getJsonPresentation(); + } + + class BooleanVariable extends Variable { + boolean value; + + public BooleanVariable(VariableOwner owner, String name, boolean v) { + value = v; + this.name = name; + code = variableMap.registerVariable(name, Boolean.class, + new Boolean(value), owner); + } + + public String getJsonPresentation() { + return "\"" + name + "\":" + (value == true ? "true" : "false"); + } + + } + + class StringVariable extends Variable { + String value; + + public StringVariable(VariableOwner owner, String name, String v) { + value = v; + this.name = name; + code = variableMap.registerVariable(name, String.class, value, + owner); + } + + public String getJsonPresentation() { + return "\"" + name + "\":\"" + value + "\""; + } + + } + + class IntVariable extends Variable { + int value; + + public IntVariable(VariableOwner owner, String name, int v) { + value = v; + this.name = name; + code = variableMap.registerVariable(name, Integer.class, + new Integer(value), owner); + } + + public String getJsonPresentation() { + return "\"" + name + "\":" + value; + } + } + + class ArrayVariable extends Variable { + String[] value; + + public ArrayVariable(VariableOwner owner, String name, String[] v) { + value = v; + this.name = name; + code = variableMap.registerVariable(name, String[].class, value, + owner); + } + + public String getJsonPresentation() { + String pres = "\"" + name + "\":["; + for (int i = 0; i < value.length;) { + pres += "\"" + value[i] + "\""; + i++; + if (i < value.length) + pres += ","; + } + pres += "]"; + return pres; + } + } + + public Set getPreCachedResources() { + return preCachedResources; + } + + public void setPreCachedResources(Set preCachedResources) { + // TODO Auto-generated method stub + + } + +} diff --git a/src/com/itmill/toolkit/terminal/gwt/server/Log.java b/src/com/itmill/toolkit/terminal/gwt/server/Log.java new file mode 100644 index 0000000000..7a84341543 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/server/Log.java @@ -0,0 +1,133 @@ +/* ************************************************************************* + + 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; + +/** + *

+ * Class providing centralized logging services. The logger defines five message + * types, and provides methods to create messages of those types. These types + * are: + *

+ * + *
    + *
  • info - Useful information generated during normal + * operation of the application. + *
  • warning - An error situation has occurred, but the + * operation was able to finish succesfully. + *
  • error - An error situation which prevented the operation + * from finishing succesfully. + *
  • debug - Internal information from the application meant + * for developers. + *
  • exception - A Java exception reported using the logger. + * Includes the exception stack trace and a possible free-form message. + *
+ * + *

+ * Currently the class offers logging only to the standard output. + *

+ * + * @author IT Mill Ltd. + * @version + * @VERSION@ + * @since 3.0 + */ +public class Log { + + private static boolean useStdOut = true; + + private static String LOG_MSG_INFO = "[INFO]"; + + private static String LOG_MSG_ERROR = "[ERROR]"; + + private static String LOG_MSG_WARN = "[WARNING]"; + + private static String LOG_MSG_DEBUG = "[DEBUG]"; + + private static String LOG_MSG_EXCEPT = "[EXCEPTION]"; + + /** + * Logs the warning message. + * + * @param message + * the Message String to be logged. + */ + protected static synchronized void warn(java.lang.String message) { + if (Log.useStdOut) + System.out.println(LOG_MSG_WARN + " " + message); + } + + /** + * Logs the debug message. + * + * @param message + * the Message String to be logged. + */ + protected static synchronized void debug(java.lang.String message) { + if (Log.useStdOut) + System.out.println(LOG_MSG_DEBUG + " " + message); + } + + /** + * Logs an info message. + * + * @param message + * the Message String to be logged. + */ + protected static synchronized void info(java.lang.String message) { + if (Log.useStdOut) + System.out.println(LOG_MSG_INFO + " " + message); + } + + /** + * Logs the Java exception and an accompanying error message. + * + * @param message + * the Message String to be logged. + * @param e + * the Exception to be logged. + */ + protected static synchronized void except(java.lang.String message, + Exception e) { + if (Log.useStdOut) { + System.out.println(LOG_MSG_EXCEPT + " " + message); + e.printStackTrace(); + } + } + + /** + * Logs the error message. + * + * @param message + * the Message String to be logged. + */ + protected static synchronized void error(java.lang.String message) { + if (Log.useStdOut) + System.out.println(LOG_MSG_ERROR + " " + message); + } +} diff --git a/src/com/itmill/toolkit/terminal/gwt/server/MultipartRequest.java b/src/com/itmill/toolkit/terminal/gwt/server/MultipartRequest.java new file mode 100644 index 0000000000..ae22f5bbbb --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/server/MultipartRequest.java @@ -0,0 +1,1332 @@ +/* ************************************************************************* + + 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.util.Hashtable; +import java.io.BufferedOutputStream; +import java.io.BufferedInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.PrintWriter; +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; +import java.io.FileOutputStream; +import java.io.UnsupportedEncodingException; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Vector; +import java.io.File; + +/** + * A Multipart form data parser. Parses an input stream and writes out any files + * found, making available a hashtable of other url parameters. As of version + * 1.17 the files can be saved to memory, and optionally written to a database, + * etc. + * + *
+ *
+ * Copyright (C)2001 Jason Pell.
+ * + *
+ * 
+ * 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.1 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
+ * Email: jasonpell@hotmail.com Url: http://www.geocities.com/jasonpell + * + *
+ * + * @author Jason Pell + * + * @version 1.18 Fixed some serious bugs. A new method readAndWrite(InputStream + * in, OutputStream out) which now does the generic processing in + * common for readAndWriteFile and readFile. The differences are that + * now the two extra bytes at the end of a file upload are processed + * once, instead of after each line. Also if an empty file is + * encountered, an outputstream is opened, but then deleted if no data + * written to it. The getCharArray method has been + * removed. Replaced by the new String(bytes, encoding) method using a + * specific encoding (Defaults to ISO-8859-1) to ensure that extended + * characters are supported. All strings are processed using this + * encoding. The addition of static methods setEncoding(String) and + * getEncoding method to allow the use of + * MultipartRequest with a specific encoding type. All + * instances of MultipartRequest will utilise the static charEncoding + * variable value, that the setEncoding method can be + * used to set. Started to introduce support for multiple file uploads + * with the same form field name, but not completed for v1.18. + * 26/06/2001 + * + * @version 1.17 A few _very_ minor fixes. Plus a cool new feature added. The + * ability to save files into memory. Thanks to Mark Latham for the + * idea and some of the code. 11/04/2001 + * @version 1.16 Added support for multiple parameter values. Also fixed + * getCharArray(...) method to support parameters with non-english + * ascii values (ascii above 127). Thanks to Stefan Schmidt & Michael + * Elvers for this. (No fix yet for reported problems with Tomcat 3.2 + * or a single extra byte appended to uploads of certain files). By + * 1.17 hopefully will have a resolution for the second problem. + * 14/03/2001 + * @version 1.15 A new parameter added, intMaxReadBytes, to allow arbitrary + * length files. Released under the LGPL (Lesser General Public + * License). 03/02/2001 + * @version 1.14 Fix for IE problem with filename being empty. This is because + * IE includes a default Content-Type even when no file is uploaded. + * 16/02/2001 + * @version 1.13 If an upload directory is not specified, then all file contents + * are sent into oblivion, but the rest of the parsing works as normal. + * @version 1.12 Fix, was allowing zero length files. Will not even create the + * output file until there is something to write. getFile(String) now + * returns null, if a zero length file was specified. 06/11/2000 + * @version 1.11 Fix, in case Content-type is not specified. + * @version 1.1 Removed dependence on Servlets. Now passes in a generic + * InputStream instead. "Borrowed" readLine from Tomcat 3.1 + * ServletInputStream class, so we can remove some of the dependencies + * on ServletInputStream. Fixed bug where a empty INPUT TYPE="FILE" + * value, would cause an exception. + * @version 1.0 Initial Release. + */ + +public class MultipartRequest { + /** + * Defines Character Encoding method here. + */ + private String charEncoding = "UTF-8"; + + // If not null, send debugging out here. + private PrintWriter debug = null; + + private Hashtable htParameters = null; + + private Hashtable htFiles = null; + + private String strBoundary = null; + + // If this Directory spec remains null, writing of files will be disabled... + private File fileOutPutDirectory = null; + + private boolean loadIntoMemory = false; + + private long intContentLength = -1; + + private long intTotalRead = -1; + + /** + * Prevent a denial of service by defining this, will never read more data. + * If Content-Length is specified to be more than this, will throw an + * exception. + * + * This limits the maximum number of bytes to the value of an int, which is + * 2 Gigabytes. + */ + public static final int MAX_READ_BYTES = Integer.MAX_VALUE; + + /** + * Defines the number of bytes to read per readLine call. 128K + */ + public static final int READ_LINE_BLOCK = 1024 * 128; + + /** + * Store a read from the input stream here. Global so we do not keep + * creating new arrays each read. + */ + private byte[] blockOfBytes = null; + + /** + * Type constant for File FILENAME. + */ + public static final int FILENAME = 0; + + /** + * Type constant for the File CONTENT_TYPE. + */ + public static final int CONTENT_TYPE = 1; + + /** + * Type constant for the File SIZE. + */ + public static final int SIZE = 2; + + /** + * Type constant for the File CONTENTS. + * + * Note: Only used for file upload to memory. + */ + public static final int CONTENTS = 3; + + /** + * This method should be called on the MultipartRequest + * itself, not on any instances of MultipartRequest, because + * this sets up the encoding for all instances of multipartrequest. You can + * set the encoding to null, in which case the default encoding will be + * applied. The default encoding if this method is not called has been set + * to ISO-8859-1, which seems to offer the best hope of support for + * international characters, such as german "Umlaut" characters. + * + *

+ * Warning: In multithreaded environments it is the responsibility + * of the implementer to make sure that this method is not called while + * another instance is being constructed. When an instance of + * MultipartRequest is constructed, it parses the input data, and uses the + * result of getEncoding method to convert between bytes and + * strings. If setEncoding method is called by another + * thread, while the private parse method is executing, the + * method will utilise this new encoding, which may cause serious problems. + *

+ */ + public void setEncoding(String enc) throws UnsupportedEncodingException { + if (enc == null || enc.trim() == "") + charEncoding = System.getProperty("file.encoding"); + else { + // This will test the encoding for validity. + new String(new byte[] { '\n' }, enc); + + charEncoding = enc; + } + } + + /** + * Gets the current encoding method. + * + * @return the encoding method. + */ + public String getEncoding() { + return charEncoding; + } + + /** + * Constructor. + * + * @param strContentTypeText + * the "Content-Type" HTTP header value. + * @param intContentLength + * the "Content-Length" HTTP header value. + * @param in + * the InputStream to read and parse. + * @param strSaveDirectory + * the temporary directory to save the file from where they can + * then be moved to wherever by the calling process. If you + * specify null for this parameter, then any files + * uploaded will be silently ignored. + * + * @exception IllegalArgumentException + * If the strContentTypeText does not contain a Content-Type + * of "multipart/form-data" or the boundary is not found. + * @exception IOException + * If the intContentLength is higher than MAX_READ_BYTES or + * strSaveDirectory is invalid or cannot be written to. + * + * @see #MAX_READ_BYTES + */ + public MultipartRequest(String strContentTypeText, int intContentLength, + InputStream in, String strSaveDirectory) + throws IllegalArgumentException, IOException { + this(null, strContentTypeText, intContentLength, in, strSaveDirectory, + MAX_READ_BYTES); + } + + /** + * Constructor. + * + * @param strContentTypeText + * the "Content-Type" HTTP header value. + * @param intContentLength + * the "Content-Length" HTTP header value. + * @param in + * the InputStream to read and parse. + * @param strSaveDirectory + * the temporary directory to save the file from where they can + * then be moved to wherever by the calling process. If you + * specify null for this parameter, then any files + * uploaded will be silently ignored. + * @param intMaxReadBytes + * Overrides the MAX_BYTES_READ value, to allow arbitrarily long + * files. + * + * @exception IllegalArgumentException + * If the strContentTypeText does not contain a Content-Type + * of "multipart/form-data" or the boundary is not found. + * @exception IOException + * If the intContentLength is higher than MAX_READ_BYTES or + * strSaveDirectory is invalid or cannot be written to. + * + * @see #MAX_READ_BYTES + */ + public MultipartRequest(String strContentTypeText, int intContentLength, + InputStream in, String strSaveDirectory, int intMaxReadBytes) + throws IllegalArgumentException, IOException { + this(null, strContentTypeText, intContentLength, in, strSaveDirectory, + intMaxReadBytes); + } + + /** + * Constructor. + * + * @param debug + * A PrintWriter that can be used for debugging. + * @param strContentTypeText + * the "Content-Type" HTTP header value. + * @param intContentLength + * the "Content-Length" HTTP header value. + * @param in + * the InputStream to read and parse. + * @param strSaveDirectory + * the temporary directory to save the file from where they can + * then be moved to wherever by the calling process. If you + * specify null for this parameter, then any files + * uploaded will be silently ignored. + * + * @exception IllegalArgumentException + * If the strContentTypeText does not contain a Content-Type + * of "multipart/form-data" or the boundary is not found. + * @exception IOException + * If the intContentLength is higher than MAX_READ_BYTES or + * strSaveDirectory is invalid or cannot be written to. + * + * @see #MAX_READ_BYTES + * @deprecated Replaced by MultipartRequest(PrintWriter, String, int, + * InputStream, int) You can specify + * MultipartRequest.MAX_READ_BYTES for the intMaxReadBytes + * parameter + */ + public MultipartRequest(PrintWriter debug, String strContentTypeText, + int intContentLength, InputStream in, String strSaveDirectory) + throws IllegalArgumentException, IOException { + this(debug, strContentTypeText, intContentLength, in, strSaveDirectory, + MAX_READ_BYTES); + + } + + /** + * Constructor - load into memory constructor + * + * @param debug + * A PrintWriter that can be used for debugging. + * @param strContentTypeText + * the "Content-Type" HTTP header value. + * @param intContentLength + * the "Content-Length" HTTP header value. + * @param in + * the InputStream to read and parse. + * @param intMaxReadBytes + * Overrides the MAX_BYTES_READ value, to allow arbitrarily long + * files. + * + * @exception IllegalArgumentException + * If the strContentTypeText does not contain a Content-Type + * of "multipart/form-data" or the boundary is not found. + * @exception IOException + * If the intContentLength is higher than MAX_READ_BYTES or + * strSaveDirectory is invalid or cannot be written to. + * + * @see #MAX_READ_BYTES + */ + public MultipartRequest(PrintWriter debug, String strContentTypeText, + int intContentLength, InputStream in, int intMaxReadBytes) + throws IllegalArgumentException, IOException { + this.loadIntoMemory = true; + + // Now initialise the object, which will actually call the parse method + // to parse multipart stream. + init(debug, strContentTypeText, intContentLength, in, intMaxReadBytes); + } + + /** + * Constructor. + * + * @param debug + * A PrintWriter that can be used for debugging. + * @param strContentTypeText + * the "Content-Type" HTTP header value. + * @param intContentLength + * the "Content-Length" HTTP header value. + * @param in + * the InputStream to read and parse. + * @param strSaveDirectory + * the temporary directory to save the file from where they can + * then be moved to wherever by the calling process. If you + * specify null for this parameter, then any files + * uploaded will be silently ignored. + * @param intMaxReadBytes + * Overrides the MAX_BYTES_READ value, to allow arbitrarily long + * files. + * + * @exception IllegalArgumentException + * If the strContentTypeText does not contain a Content-Type + * of "multipart/form-data" or the boundary is not found. + * @exception IOException + * If the intContentLength is higher than MAX_READ_BYTES or + * strSaveDirectory is invalid or cannot be written to. + * + * @see #MAX_READ_BYTES + */ + public MultipartRequest(PrintWriter debug, String strContentTypeText, + int intContentLength, InputStream in, String strSaveDirectory, + int intMaxReadBytes) throws IllegalArgumentException, IOException { + // IF strSaveDirectory == NULL, then we should ignore any files + // uploaded. + if (strSaveDirectory != null) { + fileOutPutDirectory = new File(strSaveDirectory); + if (!fileOutPutDirectory.exists()) + throw new IOException("Directory [" + strSaveDirectory + + "] is invalid."); + else if (!fileOutPutDirectory.canWrite()) + throw new IOException("Directory [" + strSaveDirectory + + "] is readonly."); + } + + // Now initialise the object, which will actually call the parse method + // to parse multipart stream. + init(debug, strContentTypeText, intContentLength, in, intMaxReadBytes); + } + + /** + * Initialise the parser. + * + * @param debug + * A PrintWriter that can be used for debugging. + * @param strContentTypeText + * the "Content-Type" HTTP header value. + * @param intContentLength + * the "Content-Length" HTTP header value. + * @param in + * the InputStream to read and parse. + * @param strSaveDirectory + * the temporary directory to save the file from where they can + * then be moved to wherever by the calling process. If you + * specify null for this parameter, then any files + * uploaded will be silently ignored. + * @param intMaxReadBytes + * Overrides the MAX_BYTES_READ value, to allow arbitrarily long + * files. + * + * @exception IllegalArgumentException + * If the strContentTypeText does not contain a Content-Type + * of "multipart/form-data" or the boundary is not found. + * @exception IOException + * If the intContentLength is higher than MAX_READ_BYTES or + * strSaveDirectory is invalid or cannot be written to. + * + * @see #MAX_READ_BYTES + */ + private void init(PrintWriter debug, String strContentTypeText, + int intContentLength, InputStream in, int intMaxReadBytes) + throws IllegalArgumentException, IOException { + // saves reference to debug stream for later. + this.debug = debug; + + if (strContentTypeText != null + && strContentTypeText.startsWith("multipart/form-data") + && strContentTypeText.indexOf("boundary=") != -1) + strBoundary = strContentTypeText.substring( + strContentTypeText.indexOf("boundary=") + + "boundary=".length()).trim(); + else { + // + debug("ContentType = " + strContentTypeText); + throw new IllegalArgumentException("Invalid Content Type."); + } + + this.intContentLength = intContentLength; + // FIX: 115 + if (intContentLength > intMaxReadBytes) + throw new IOException("Content Length Error (" + intContentLength + + " > " + intMaxReadBytes + ")"); + + // Instantiate the hashtable... + htParameters = new Hashtable(); + htFiles = new Hashtable(); + blockOfBytes = new byte[READ_LINE_BLOCK]; + + // Now parse the data. + parse(new BufferedInputStream(in)); + + // No need for this once parse is complete. + this.blockOfBytes = null; + this.debug = null; + this.strBoundary = null; + } + + /** + * Gets the value of the strName URLParameter. If more than one value for a + * particular Parameter, will return the first. If an error occurs will + * return null. + * + * @param strName + * the form field name, used to upload the file. This identifies + * the formfield location in the storage facility. + * @return the value of the URL Parameter. + */ + public String getURLParameter(String strName) { + Object value = htParameters.get(strName); + if (value instanceof Vector) + return (String) ((Vector) value).firstElement(); + else + return (String) htParameters.get(strName); + } + + /** + * Gets an enumeration of all values for the strName parameter. Even if a + * single value for, will always return an enumeration, although it may + * actually be empty if no value was encountered for strName or it is an + * invalid parameter name. + * + * @param strName + * the form field name, used to upload the file. This identifies + * the formfield location in the storage facility. + * @return the enumeration of all values. + */ + public Enumeration getURLParameters(String strName) { + Object value = htParameters.get(strName); + if (value instanceof Vector) + return ((Vector) value).elements(); + else { + Vector vector = new Vector(); + if (value != null) + vector.addElement(value); + return vector.elements(); + } + } + + /** + * Gets the enumeration of all URL Parameters for the current HTTP Request. + * + * @return the enumeration of URl Parameters. + */ + public Enumeration getParameterNames() { + return htParameters.keys(); + } + + /** + * Gets the enumeration of all INPUT TYPE=FILE parameter NAMES as + * encountered during the upload. + * + * @return + */ + public Enumeration getFileParameterNames() { + return htFiles.keys(); + } + + /** + * Returns the Content-Type of a file. + * + * @see #getFileParameter(java.lang.String, int) + */ + public String getContentType(String strName) { + // Can cast null, it will be ignored. + return (String) getFileParameter(strName, CONTENT_TYPE); + } + + /** + * If files were uploaded into memory, this method will retrieve the + * contents of the file as a InputStream. + * + * @param strName + * the form field name, used to upload the file. This identifies + * the formfield location in the storage facility. + * @return the contents of the file as a InputStream, or null if not file + * uploaded, or file uploaded to file system directory. + * @see #getFileParameter(java.lang.String, int) + */ + public InputStream getFileContents(String strName) { + Object obj = getFileParameter(strName, CONTENTS); + if (obj != null) + return new ByteArrayInputStream((byte[]) obj); + else + return null; + } + + /** + * Returns a File reference to the uploaded file. This reference is to the + * files uploaded location, and allows you to read/move/delete the file. + * This method is only of use, if files were uploaded to the file system. + * Will return null if uploaded to memory, in which case you should use + * getFileContents(strName) instead. + * + * @param strName + * the form field name, used to upload the file. This identifies + * the formfield location in the storage facility. + * @return a null file reference if a call to getFileSize(strName) returns + * zero or files were uploaded to memory. + * @see #getFileSize(java.lang.String) + * @see #getFileContents(java.lang.String) + * @see #getFileSystemName(java.lang.String) + */ + public File getFile(String strName) { + String filename = getFileSystemName(strName); + // Fix: If fileOutPutDirectory is null, then we are ignoring any file + // contents, so we must return null. + if (filename != null && getFileSize(strName) > 0 + && fileOutPutDirectory != null) + return new File(fileOutPutDirectory, filename); + else + return null; + } + + /** + * Gets the file system basename of an uploaded file. + * + * @param strName + * the form field name, used to upload the file. This identifies + * the formfield location in the storage facility. + * @return null if strName not found. + * + * @see #getFileParameter(java.lang.String, int) + */ + public String getFileSystemName(String strName) { + // Can cast null, it will be ignored. + return (String) getFileParameter(strName, FILENAME); + } + + /** + * Returns the File Size of a uploaded file. + * + * @param strName + * the form field name, used to upload the file. This identifies + * the formfield location in the storage facility. + * @return -1 if file size not defined. + * + * @see #getFileParameter(java.lang.String, int) + */ + public long getFileSize(String strName) { + Object obj = getFileParameter(strName, SIZE); + if (obj != null) + return ((Long) obj).longValue(); + else + return (long) -1; + } + + /** + * Access an attribute of a file upload parameter record. + *

+ * The getFileSystemName(String strName),getFileSize(String + * strName),getContentType(String strName), getContents(String strName) + * methods all use this method passing in a different type argument. + *

+ * + *

+ * Note: This class has been changed to provide for future + * functionality where you will be able to access all files uploaded, even + * if they are uploaded using the same form field name. At this point + * however, only the first file uploaded via a form field name is + * accessible. + *

+ * + * @param strName + * the form field name, used to upload the file. This identifies + * the formfield location in the storage facility. + * + * @param type + * What attribute you want from the File Parameter. The following + * types are supported: MultipartRequest.FILENAME, + * MultipartRequest.CONTENT_TYPE, MultipartRequest.SIZE, + * MultipartRequest.CONTENTS + * + * @see #getContentType(java.lang.String) + * @see #getFileSize(java.lang.String) + * @see #getFileContents(java.lang.String) + * @see #getFileSystemName(java.lang.String) + */ + public Object getFileParameter(String strName, int type) { + Object[] objArray = null; + Object value = htFiles.get(strName); + if (value instanceof Vector) + objArray = (Object[]) ((Vector) value).firstElement(); + else + objArray = (Object[]) htFiles.get(strName); + + // Now ensure valid value. + if (objArray != null && type >= FILENAME && type <= CONTENTS) + return objArray[type]; + else + return null; + } + + /** + * This is the main parse method. + * + * @param in + * the InputStream to read and parse. + * @throws IOException + * If the intContentLength is higher than MAX_READ_BYTES or + * strSaveDirectory is invalid or cannot be written to. + */ + private void parse(InputStream in) throws IOException { + String strContentType = null; + String strName = null; + String strFilename = null; + String strLine = null; + int read = -1; + + // First run through, check that the first line is a boundary, otherwise + // throw a exception as format incorrect. + read = readLine(in, blockOfBytes); + strLine = read > 0 ? new String(blockOfBytes, 0, read, charEncoding) + : null; + + // Must be boundary at top of loop, otherwise we have finished. + if (strLine == null || strLine.indexOf(this.strBoundary) == -1) + // Just exit. Exception would be overkill + return; + // throw new IOException("Invalid Form Data, no boundary encountered."); + + // At the top of loop, we assume that the Content-Disposition line is + // next, otherwise we are at the end. + while (true) { + // Get Content-Disposition line. + read = readLine(in, blockOfBytes); + if (read <= 0) + break; // Nothing to do. + else { + strLine = new String(blockOfBytes, 0, read, charEncoding); + + // Mac IE4 adds extra line after last boundary - 1.21 + if (strLine == null || strLine.length() == 0 + || strLine.trim().length() == 0) + break; + + strName = trimQuotes(getValue("name", strLine)); + // If this is not null, it indicates that we are processing a + // filename. + strFilename = trimQuotes(getValue("filename", strLine)); + // Now if not null, strip it of any directory information. + + if (strFilename != null) { + // Fix: did not check whether filename was empty string + // indicating FILE contents were not passed. + if (strFilename.length() > 0) { + // Need to get the content type. + read = readLine(in, blockOfBytes); + strLine = read > 0 ? new String(blockOfBytes, 0, read, + charEncoding) : null; + + strContentType = "application/octet-stream"; + // Fix 1.11: If not null AND strLine.length() is long + // enough. + if (strLine != null + && strLine.length() > "Content-Type: ".length()) + strContentType = strLine.substring("Content-Type: " + .length());// Changed 1.13 + } else { + // FIX 1.14: IE problem with empty filename. + read = readLine(in, blockOfBytes); + strLine = read > 0 ? new String(blockOfBytes, 0, read, + charEncoding) : null; + + if (strLine != null + && strLine.startsWith("Content-Type:")) + readLine(in, blockOfBytes); + } + } + + // Ignore next line, as it should be blank. + readLine(in, blockOfBytes); + + // No filename specified at all. + if (strFilename == null) { + String param = readParameter(in); + addParameter(strName, param); + } else { + if (strFilename.length() > 0) { + long filesize = -1; + // Will remain null for read onto file system uploads. + byte[] contentsOfFile = null; + + // Get the BASENAME version of strFilename. + strFilename = getBasename(strFilename); + + // Are we loading files into memory instead of the + // filesystem? + if (loadIntoMemory) { + contentsOfFile = readFile(in); + if (contentsOfFile != null) + filesize = contentsOfFile.length; + } else + // Read the file onto file system. + filesize = readAndWriteFile(in, strFilename); + + // Fix 1.18 for multiple FILE parameter values. + if (filesize > 0) + addFileParameter(strName, new Object[] { + strFilename, strContentType, + new Long(filesize), contentsOfFile }); + else + // Zero length file. + addFileParameter(strName, new Object[] { + strFilename, null, new Long(0), null }); + } else // Fix: FILE INPUT TYPE, but no file passed as + // input... + { + addFileParameter(strName, new Object[] { null, null, + null, null }); + readLine(in, blockOfBytes); + } + } + } + }// while + } + + /** + * So we can put the logic for supporting multiple parameters with the same + * form field name in the one location. + * + * @param strName + * the form field name, used to upload the file. This identifies + * the formfield location in the storage facility. + * @param value + * the form field value. + */ + private void addParameter(String strName, String value) { + // Fix NPE in case of null name + if (strName == null) + return; + + // Fix 1.16: for multiple parameter values. + Object objParms = htParameters.get(strName); + + // Adds an new entry to the param vector. + if (objParms instanceof Vector) + ((Vector) objParms).addElement(value); + else if (objParms instanceof String)// There is only one entry, so we + // create a vector! + { + Vector vecParms = new Vector(); + vecParms.addElement(objParms); + vecParms.addElement(value); + + htParameters.put(strName, vecParms); + } else + // first entry for strName. + htParameters.put(strName, value); + } + + /** + * So we can put the logic for supporting multiple files with the same form + * field name in the one location. + * + * Assumes that this method will never be called with a null fileObj or + * strFilename. + * + * @param strName + * the form field name, used to upload the file. This identifies + * the formfield location in the storage facility. + * @param fileObj + */ + private void addFileParameter(String strName, Object[] fileObj) { + Object objParms = htFiles.get(strName); + + // Add an new entry to the param vector. + if (objParms instanceof Vector) + ((Vector) objParms).addElement(fileObj); + else if (objParms instanceof Object[])// There is only one entry, so + // we create a vector! + { + Vector vecParms = new Vector(); + vecParms.addElement(objParms); + vecParms.addElement(fileObj); + + htFiles.put(strName, vecParms); + } else + // first entry for strName. + htFiles.put(strName, fileObj); + } + + /** + * Reads the parameters, assume already passed Content-Disposition and blank + * line. + * + * @param in + * the InputStream to read and parse. + * @return the value read in. + * @throws IOException + * if an error occurs writing the file. + */ + private String readParameter(InputStream in) throws IOException { + StringBuffer buf = new StringBuffer(); + int read = -1; + + String line = null; + while (true) { + read = readLine(in, blockOfBytes); + if (read < 0) + throw new IOException("Stream ended prematurely."); + + // Change v1.18: Only instantiate string once for performance + // reasons. + line = new String(blockOfBytes, 0, read, charEncoding); + if (read < blockOfBytes.length + && line.indexOf(this.strBoundary) != -1) + break; // Boundary found, we need to finish up. + else + buf.append(line); + } + + if (buf.length() > 0) + buf.setLength(getLengthMinusEnding(buf)); + return buf.toString(); + } + + /** + * Read from in, write to out, minus last two line ending bytes. + * + * @param in + * the InputStream to read and parse. + * @param out + * the OutputStream. + * @throws IOException + * if an error occurs writing the file. + */ + private long readAndWrite(InputStream in, OutputStream out) + throws IOException { + long fileSize = 0; + int read = -1; + + // This variable will be assigned the bytes actually read. + byte[] secondLineOfBytes = new byte[blockOfBytes.length]; + // So we do not have to keep creating the second array. + int sizeOfSecondArray = 0; + + while (true) { + read = readLine(in, blockOfBytes); + if (read < 0) + throw new IOException("Stream ended prematurely."); + + // Found boundary. + if (read < blockOfBytes.length + && new String(blockOfBytes, 0, read, charEncoding) + .indexOf(this.strBoundary) != -1) { + // Writes the line, minus any line ending bytes. + // The secondLineOfBytes will NEVER BE NON-NULL if out==null, so + // there is no need to included this in the test + if (sizeOfSecondArray != 0) { + // Only used once, so declare here. + int actualLength = getLengthMinusEnding(secondLineOfBytes, + sizeOfSecondArray); + if (actualLength > 0 && out != null) { + out.write(secondLineOfBytes, 0, actualLength); + // Updates the file size. + fileSize += actualLength; + } + } + break; + } else { + // Writes the out previous line. + // The sizeOfSecondArray will NEVER BE ZERO if out==null, so + // there is no need to included this in the test + if (sizeOfSecondArray != 0) { + out.write(secondLineOfBytes, 0, sizeOfSecondArray); + // Updates the file size. + fileSize += sizeOfSecondArray; + } + + // out will always be null, so there is no need to reset + // sizeOfSecondArray to zero each time. + if (out != null) { + // Copy the read bytes into the array. + System.arraycopy(blockOfBytes, 0, secondLineOfBytes, 0, + read); + // That is how many bytes to read from the secondLineOfBytes + sizeOfSecondArray = read; + } + } + } + + // Returns the number of bytes written to outstream. + return fileSize; + } + + /** + * Reads a Multipart section that is a file type. Assumes that the + * Content-Disposition/Content-Type and blank line have already been + * processed. So we read until we hit a boundary, then close file and + * return. + * + * @param in + * the InputStream to read and parse. + * @param strFilename + * the FileSystemName of the file. + * @return the number of bytes read. + * @throws IOException + * if an error occurs writing the file. + */ + private long readAndWriteFile(InputStream in, String strFilename) + throws IOException { + // Stores a reference to this, as we may need to delete it later. + File outFile = new File(fileOutPutDirectory, strFilename); + + BufferedOutputStream out = null; + // Do not bother opening a OutputStream, if we cannot even write the + // file. + if (fileOutPutDirectory != null) + out = new BufferedOutputStream(new FileOutputStream(outFile)); + + long count = readAndWrite(in, out); + // Count would NOT be larger than zero if out was null. + if (count > 0) { + out.flush(); + out.close(); + } else { + out.close(); + // Deletes the file as empty. We should be able to delete it, if we + // can open it! + outFile.delete(); + } + return count; + } + + /** + * If the fileOutPutDirectory wasn't specified, just read the file to + * memory. + * + * @param in + * the InputStream to read and parse. + * @return contents of file, from which you can garner the size as well. + * @throws IOException + * if the writing failed due to input/output error. + */ + private byte[] readFile(InputStream in) throws IOException { + // In this case, we do not need to worry about a outputdirectory. + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + long count = readAndWrite(in, out); + // Count would NOT be larger than zero if out was null. + if (count > 0) { + // Return contents of file to parse method for inclusion in htFiles + // object. + return out.toByteArray(); + } else + return null; + } + + /** + * Gets the length of the line minus line ending. + * + * @param byteLine + * @param endOfArray + * This is because in many cases the byteLine will have garbage + * data at the end, so we act as though the actual end of the + * array is this parameter. If you want to process the complete + * byteLine, specify byteLine.length as the endOfArray parameter. + * @return the length. + */ + private static final int getLengthMinusEnding(byte byteLine[], + int endOfArray) { + if (byteLine == null) + return 0; + + if (endOfArray >= 2 && byteLine[endOfArray - 2] == '\r' + && byteLine[endOfArray - 1] == '\n') + return endOfArray - 2; + else if (endOfArray >= 1 && byteLine[endOfArray - 1] == '\n' + || byteLine[endOfArray - 1] == '\r') + return endOfArray - 1; + else + return endOfArray; + } + + /** + * + * @param buf + * @return + */ + private static final int getLengthMinusEnding(StringBuffer buf) { + if (buf.length() >= 2 && buf.charAt(buf.length() - 2) == '\r' + && buf.charAt(buf.length() - 1) == '\n') + return buf.length() - 2; + else if (buf.length() >= 1 && buf.charAt(buf.length() - 1) == '\n' + || buf.charAt(buf.length() - 1) == '\r') + return buf.length() - 1; + else + return buf.length(); + } + + /** + * Reads at most READ_BLOCK blocks of data, or a single line whichever is + * smaller. Returns -1, if nothing to read, or we have reached the specified + * content-length. + * + * Assumes that bytToBeRead.length indicates the block size to read. + * + * @param in + * the InputStream to read and parse. + * @param bytesToBeRead + * the bytes to be read. + * @return -1 if stream has ended, before a newline encountered (should + * never happen) OR we have read past the Content-Length specified. + * (Should also not happen). Otherwise return the number of + * characters read. You can test whether the number returned is less + * than bytesToBeRead.length, which indicates that we have read the + * last line of a file or parameter or a border line, or some other + * formatting stuff. + * @throws IOException + * if the writing failed due to input/output error. + */ + private int readLine(InputStream in, byte[] bytesToBeRead) + throws IOException { + // Ensure that there is still stuff to read... + if (intTotalRead >= intContentLength) + return -1; + + // Get the length of what we are wanting to read. + int length = bytesToBeRead.length; + + // End of content, but some servers (apparently) may not realise this + // and end the InputStream, so + // we cover ourselves this way. + if (length > (intContentLength - intTotalRead)) + length = (int) (intContentLength - intTotalRead); // So we only + // read the data + // that is left. + + int result = readLine(in, bytesToBeRead, 0, length); + // Only if we get actually read something, otherwise something weird has + // happened, such as the end of stream. + if (result > 0) + intTotalRead += result; + + return result; + } + + /** + * This needs to support the possibility of a / or a \ separator. + * + * @param strFilename + * the FileSystemName of the file. + * @return the strFilename after removing all characters before the last + * occurence of / or \. + */ + private static final String getBasename(String strFilename) { + if (strFilename == null) + return strFilename; + + int intIndex = strFilename.lastIndexOf("/"); + if (intIndex == -1 || strFilename.lastIndexOf("\\") > intIndex) + intIndex = strFilename.lastIndexOf("\\"); + + if (intIndex != -1) + return strFilename.substring(intIndex + 1); + else + return strFilename; + } + + /** + * Trims any quotes from the start and end of a string. + * + * @param strItem + * @return the trimmed string. + */ + private static final String trimQuotes(String strItem) { + // Saves having to go any further.... + if (strItem == null || strItem.indexOf("\"") == -1) + return strItem; + + // Gets the rid of any whitespace.. + strItem = strItem.trim(); + + if (strItem.charAt(0) == '\"') + strItem = strItem.substring(1); + + if (strItem.charAt(strItem.length() - 1) == '\"') + strItem = strItem.substring(0, strItem.length() - 1); + + return strItem; + } + + /** + * Format of string name=value; name=value; If not found, will return null. + * + * @param strName + * the form field name, used to upload the file. This identifies + * the formfield location in the storage facility. + * @param strToDecode + * + */ + private static final String getValue(String strName, String strToDecode) { + strName = strName + "="; + + int startIndexOf = 0; + while (startIndexOf < strToDecode.length()) { + int indexOf = strToDecode.indexOf(strName, startIndexOf); + // Ensure either first name, or a space or ; precedes it. + if (indexOf != -1) { + if (indexOf == 0 + || Character.isWhitespace(strToDecode + .charAt(indexOf - 1)) + || strToDecode.charAt(indexOf - 1) == ';') { + int endIndexOf = strToDecode.indexOf(";", indexOf + + strName.length()); + if (endIndexOf == -1) // May return an empty string... + return strToDecode + .substring(indexOf + strName.length()); + else + return strToDecode.substring( + indexOf + strName.length(), endIndexOf); + } else + startIndexOf = indexOf + strName.length(); + } else + return null; + } + return null; + } + + /** + * Tomcat's ServletInputStream.readLine(byte[],int,int) Slightly Modified + * to utilise in.read()
+ * Reads the input stream, one line at a time. Starting at an offset, reads + * bytes into an array, until it reads a certain number of bytes or reaches + * a newline character, which it reads into the array as well. + * + *

+ * This method does not returns -1 if it reaches the end of + * the input stream before reading the maximum number of bytes, it returns + * -1, if no bytes read. + * + * @param in + * the InputStream to read and parse. + * @param b + * an array of bytes into which data is read. + * + * @param off + * an integer specifying the character at which this method + * begins reading. + * + * @param len + * an integer specifying the maximum number of bytes to read. + * + * @return an integer specifying the actual number of bytes read, or -1 if + * the end of the stream is reached. + * + * @throws IOException + * if an input or output exception has occurred + * + * + * Note: We have a problem with Tomcat reporting an erroneous number of + * bytes, so we need to check this. This is the method where we get an + * infinite loop, but only with binary files. + */ + private int readLine(InputStream in, byte[] b, int off, int len) + throws IOException { + if (len <= 0) + return 0; + + int count = 0, c; + + while ((c = in.read()) != -1) { + b[off++] = (byte) c; + count++; + if (c == '\n' || count == len) + break; + } + + return count > 0 ? count : -1; + } + + /** + * Prints the given debugging message. + * + * @param x + * the message to print. + */ + protected void debug(String x) { + if (debug != null) { + debug.println(x); + debug.flush(); + } + } + + /** + * Gets the Html Table.For debugging. + */ + public String getHtmlTable() { + StringBuffer sbReturn = new StringBuffer(); + + sbReturn.append("

Parameters

"); + sbReturn + .append("\n"); + for (Enumeration e = getParameterNames(); e.hasMoreElements();) { + String strName = (String) e.nextElement(); + sbReturn.append("\n" + ""); + + sbReturn.append(""); + } + sbReturn.append("
NameValue
" + strName + ""); + for (Enumeration f = getURLParameters(strName); f.hasMoreElements();) { + String value = (String) f.nextElement(); + sbReturn.append(""); + } + sbReturn.append("
" + value + "
"); + + sbReturn.append("

File Parameters

"); + + sbReturn + .append("\n"); + for (Enumeration e = getFileParameterNames(); e.hasMoreElements();) { + String strName = (String) e.nextElement(); + + sbReturn + .append("\n" + + "" + + ""); + + if (loadIntoMemory) + sbReturn.append(""); + else + sbReturn.append(""); + + sbReturn + .append("" + + "" + ""); + } + sbReturn.append("
NameFilenamePathContent TypeSize
" + + strName + + "" + + (getFileSystemName(strName) != null ? getFileSystemName(strName) + : "") + "" + + (getFileSize(strName) > 0 ? "in memory" : "") + + "" + + (getFile(strName) != null ? getFile(strName) + .getAbsolutePath() : "") + "" + + (getContentType(strName) != null ? getContentType(strName) + : "") + + "" + + (getFileSize(strName) != -1 ? getFileSize(strName) + + "" + : "") + "
"); + + return sbReturn.toString(); + } +} diff --git a/src/com/itmill/toolkit/terminal/gwt/server/ServletMultipartRequest.java b/src/com/itmill/toolkit/terminal/gwt/server/ServletMultipartRequest.java new file mode 100644 index 0000000000..a0352e5944 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/server/ServletMultipartRequest.java @@ -0,0 +1,139 @@ +/* ************************************************************************* + + 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.IOException; + +import javax.servlet.http.HttpServletRequest; + +/** + * This class wraps the MultipartRequest class by Jason Pell for the Servlet + * environment. + * + * @author IT Mill Ltd + * @version + * @VERSION@ + * @since 3.0 + */ +public class ServletMultipartRequest extends MultipartRequest { + /** + * Constructor wrapper, unwraps the InputStream, content type and content + * length from the servlet request object. + * + * @param request + * the HttpServletRequest will be used to initialise the + * MultipartRequest super class. + * @param strSaveDirectory + * the temporary directory to save the file from where they can + * then be moved to wherever by the calling process. If you + * specify null for this parameter, then any files + * uploaded will be silently ignored. + * + * @throws IllegalArgumentException + * If the request.getContentType() does not contain a + * Content-Type of "multipart/form-data" or the boundary is not + * found. + * @throws IOException + * If the request.getContentLength() is higher than + * MAX_READ_BYTES or strSaveDirectory is invalid or cannot be + * written to. + * + * @see MultipartRequest#MAX_READ_BYTES + */ + public ServletMultipartRequest(HttpServletRequest request, + String strSaveDirectory) throws IllegalArgumentException, + IOException { + super(null, request.getContentType(), request.getContentLength(), + request.getInputStream(), strSaveDirectory, + MultipartRequest.MAX_READ_BYTES); + } + + /** + * Constructor wrapper, unwraps the InputStream, content type and content + * lenght from the servlet request object. Also allow to explicitly set the + * max permissable length of the request. + * + * @param request + * the HttpServletRequest will be used to initialise the + * MultipartRequest super class. + * @param strSaveDirectory + * the temporary directory to save the file from where they can + * then be moved to wherever by the calling process. If you + * specify null for this parameter, then any files + * uploaded will be silently ignored. + * @param intMaxReadBytes + * Overrides the MAX_BYTES_READ value, to allow arbitrarily long + * files. + * + * @throws IllegalArgumentException + * If the request.getContentType() does not contain a + * Content-Type of "multipart/form-data" or the boundary is not + * found. + * @throws IOException + * If the request.getContentLength() is higher than + * MAX_READ_BYTES or strSaveDirectory is invalid or cannot be + * written to. + * + * @see MultipartRequest#MAX_READ_BYTES + */ + public ServletMultipartRequest(HttpServletRequest request, + String strSaveDirectory, int intMaxReadBytes) + throws IllegalArgumentException, IOException { + super(null, request.getContentType(), request.getContentLength(), + request.getInputStream(), strSaveDirectory, intMaxReadBytes); + } + + /** + * Constructor wrapper for loading the request into memory rather than + * temp-file. + * + * @param request + * the HttpServletRequest will be used to initialise the + * MultipartRequest super class. + * @param intMaxReadBytes + * Overrides the MAX_BYTES_READ value, to allow arbitrarily long + * files. + * + * @throws IllegalArgumentException + * If the request.getContentType() does not contain a + * Content-Type of "multipart/form-data" or the boundary is not + * found. + * @throws IOException + * If the request.getContentLength() is higher than + * MAX_READ_BYTES or strSaveDirectory is invalid or cannot be + * written to. + * + * @see MultipartRequest#MAX_READ_BYTES + */ + public ServletMultipartRequest(HttpServletRequest request, + int intMaxReadBytes) throws IllegalArgumentException, IOException { + super(null, request.getContentType(), request.getContentLength(), + request.getInputStream(), intMaxReadBytes); + } +} diff --git a/src/com/itmill/toolkit/terminal/gwt/server/WebApplicationContext.java b/src/com/itmill/toolkit/terminal/gwt/server/WebApplicationContext.java new file mode 100644 index 0000000000..0c53f87db6 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/server/WebApplicationContext.java @@ -0,0 +1,261 @@ +/* ************************************************************************* + + 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.File; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.WeakHashMap; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import com.itmill.toolkit.Application; +import com.itmill.toolkit.service.ApplicationContext; +import com.itmill.toolkit.ui.Window; + +/** + * Web application context for the IT Mill Toolkit applications. + * + * @author IT Mill Ltd. + * @version + * @VERSION@ + * @since 3.1 + */ +public class WebApplicationContext implements ApplicationContext { + + private List listeners; + + private HttpSession session; + + private WeakHashMap formActions = new WeakHashMap(); + + /** + * Creates a new Web Application Context. + * + * @param session + * the HTTP session. + */ + WebApplicationContext(HttpSession session) { + this.session = session; + } + + /** + * Gets the form action for given window. + *

+ * By default, this action is "", which preserves the current url. Commonly + * this is wanted to be set to application.getUrl.toString or + * window.getUrl.toString in order to clean any local links + * or parameters set from the action. + *

+ * + * @param window + * the Window for which the action is queried. + * @return the Action to be set into Form action attribute. + */ + public String getWindowFormAction(Window window) { + String action = (String) formActions.get(window); + return action == null ? "" : action; + } + + /** + * Sets the form action for given window. + *

+ * By default, this action is "", which preserves the current url. Commonly + * this is wanted to be set to application.getUrl.toString or + * window.getUrl.toString in order to clean any local links + * or parameters set from the action. + *

+ * + * @param window + * the Window for which the action is set. + * @param action + * the New action for the window. + */ + public void setWindowFormAction(Window window, String action) { + if (action == null || action == "") + formActions.remove(window); + else + formActions.put(window, action); + } + + /** + * Gets the application context base directory. + * + * @see com.itmill.toolkit.service.ApplicationContext#getBaseDirectory() + */ + public File getBaseDirectory() { + String realPath = ApplicationServlet.getResourcePath(session + .getServletContext(), "/"); + if (realPath == null) + return null; + return new File(realPath); + } + + /** + * Gets the http-session application is running in. + * + * @return HttpSession this application context resides in. + */ + public HttpSession getHttpSession() { + return session; + } + + /** + * Gets the applications in this context. + * + * @see com.itmill.toolkit.service.ApplicationContext#getApplications() + */ + public Collection getApplications() { + LinkedList applications = (LinkedList) session + .getAttribute(ApplicationServlet.SESSION_ATTR_APPS); + + return Collections + .unmodifiableCollection(applications == null ? (new LinkedList()) + : applications); + } + + /** + * Gets the application context for HttpSession. + * + * @param session + * the HTTP session. + * @return the application context for HttpSession. + */ + static public WebApplicationContext getApplicationContext( + HttpSession session) { + return new WebApplicationContext(session); + } + + /** + * Returns true if and only if the argument is not + * null and is a Boolean object that represents the same + * boolean value as this object. + * + * @param obj + * the object to compare with. + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + return session.equals(obj); + } + + /** + * Returns the hash code value . + * + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return session.hashCode(); + } + + /** + * Adds the transaction listener to this context. + * + * @see com.itmill.toolkit.service.ApplicationContext#addTransactionListener(com.itmill.toolkit.service.ApplicationContext.TransactionListener) + */ + public void addTransactionListener(TransactionListener listener) { + if (this.listeners == null) + this.listeners = new LinkedList(); + this.listeners.add(listener); + } + + /** + * Removes the transaction listener from this context. + * + * @see com.itmill.toolkit.service.ApplicationContext#removeTransactionListener(com.itmill.toolkit.service.ApplicationContext.TransactionListener) + */ + public void removeTransactionListener(TransactionListener listener) { + if (this.listeners != null) + this.listeners.remove(listener); + + } + + /** + * Notifies the transaction start. + * + * @param application + * @param request + * the HTTP request. + */ + protected void startTransaction(Application application, + HttpServletRequest request) { + if (this.listeners == null) + return; + for (Iterator i = this.listeners.iterator(); i.hasNext();) { + ((ApplicationContext.TransactionListener) i.next()) + .transactionStart(application, request); + } + } + + /** + * Notifies the transaction end. + * + * @param application + * @param request + * the HTTP request. + */ + protected void endTransaction(Application application, + HttpServletRequest request) { + if (this.listeners == null) + return; + + LinkedList exceptions = null; + for (Iterator i = this.listeners.iterator(); i.hasNext();) + try { + ((ApplicationContext.TransactionListener) i.next()) + .transactionEnd(application, request); + } catch (RuntimeException t) { + if (exceptions == null) + exceptions = new LinkedList(); + exceptions.add(t); + } + + // If any runtime exceptions occurred, throw a combined exception + if (exceptions != null) { + StringBuffer msg = new StringBuffer(); + for (Iterator i = listeners.iterator(); i.hasNext();) { + RuntimeException e = (RuntimeException) i.next(); + if (msg.length() == 0) + msg.append("\n\n--------------------------\n\n"); + msg.append(e.getMessage() + "\n"); + StringWriter trace = new StringWriter(); + e.printStackTrace(new PrintWriter(trace, true)); + msg.append(trace.toString()); + } + throw new RuntimeException(msg.toString()); + } + } + +} diff --git a/src/com/itmill/toolkit/terminal/gwt/server/WebBrowser.java b/src/com/itmill/toolkit/terminal/gwt/server/WebBrowser.java new file mode 100644 index 0000000000..f89db74c0a --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/server/WebBrowser.java @@ -0,0 +1,703 @@ +/* ************************************************************************* + + 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 com.itmill.toolkit.terminal.Terminal; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Locale; + +/** + * Web browser terminal type. + * + * This class implements web browser properties, which declare the features of + * the web browser. + * + * @author IT Mill Ltd. + * @version + * @VERSION@ + * @since 3.0 + */ +public class WebBrowser implements Terminal { + + private static WebBrowser DEFAULT = new WebBrowser(); + + /** + * Content type. + */ + private String contentType = "text/html; charset=utf-8"; + + /** + * Holds the collection of accepted locales. + */ + private Collection locales = new ArrayList(); + + /** + * Holds value of property browserApplication. + */ + private String browserApplication = null; + + /** + * Should the client side checkking be done. + */ + private boolean performClientCheck = true; + + /** + * Holds value for property isClientSideChecked. + */ + private boolean clientSideChecked = false; + + /** + * Holds value of property javaScriptVersion. + */ + private JavaScriptVersion javaScriptVersion = JAVASCRIPT_UNCHECKED; + + /** + * Holds value of property javaEnabled. + */ + private boolean javaEnabled = false; + + /** + * Holds value of property frameSupport. + */ + private boolean frameSupport = false; + + /** + * Holds value of property markup version. + */ + private MarkupVersion markupVersion = MARKUP_HTML_3_2; + + /** + * Pixel width of the terminal screen. + */ + private int screenWidth = -1; + + /** + * Pixel height of the terminal screen. + */ + private int screenHeight = -1; + + /** + * Constuctor with some autorecognition capabilities Retrieves all + * capability information reported in http request headers: + *
    + *
  • User web browser (User-Agent)
  • + *
  • Supported locale(s)
  • + *
+ */ + + /** + * Constructor WebBrowserType. Creates a default WebBrowserType instance. + */ + public WebBrowser() { + } + + /** + * Gets the name of the default theme. + * + * @return the Name of the terminal window. + */ + public String getDefaultTheme() { + return ApplicationServlet.DEFAULT_THEME; + } + + /** + * Gets the name and version of the web browser application. This is the + * version string reported by the web-browser in http headers. + * + * @return the Web browser application. + */ + public String getBrowserApplication() { + return this.browserApplication; + } + + /** + * Gets the version of the supported Java Script by the browser. + * + * Null if the Java Script is not supported. + * + * @return the Version of the supported Java Script. + */ + public JavaScriptVersion getJavaScriptVersion() { + return this.javaScriptVersion; + } + + /** + * Does the browser support frames ? + * + * @return true if the browser supports frames, otherwise + * false. + */ + public boolean isFrameSupport() { + return this.frameSupport; + } + + /** + * Sets the browser frame support. + * + * @param frameSupport + * True if the browser supports frames, False if not. + */ + public void setFrameSupport(boolean frameSupport) { + this.frameSupport = frameSupport; + } + + /** + * Gets the supported markup language. + * + * @return the Supported markup language + */ + public MarkupVersion getMarkupVersion() { + return this.markupVersion; + } + + /** + * Gets the height of the terminal window in pixels. + * + * @return the Height of the terminal window. + */ + public int getScreenHeight() { + return this.screenHeight; + } + + /** + * Gets the width of the terminal window in pixels. + * + * @return the Width of the terminal window. + */ + public int getScreenWidth() { + return this.screenWidth; + } + + /** + * Gets the default locale requested by the browser. + * + * @return the Default locale. + */ + public Locale getDefaultLocale() { + if (this.locales.isEmpty()) + return null; + return (Locale) this.locales.iterator().next(); + } + + /** + * Hash code composed of the properties of the web browser type. + * + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return toString().hashCode(); + } + + /** + * Tests the equality of the properties for two web browser types. + * + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (obj != null && obj instanceof WebBrowser) { + return toString().equals(obj.toString()); + } + return false; + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + + String localeString = "["; + for (Iterator i = this.locales.iterator(); i.hasNext(); localeString += ",") { + localeString += ((Locale) i.next()).toString(); + } + localeString += "]"; + + // Returns catenation of the properties + return "Browser:" + this.browserApplication + ", " + "Locales:" + + localeString + ", " + "Frames:" + this.frameSupport + ", " + + "JavaScript:" + this.javaScriptVersion + ", " + "Java: " + + this.javaEnabled + ", " + "Markup:" + this.markupVersion + + ", " + "Height:" + this.screenHeight + ", " + "Width:" + + this.screenWidth + ", ClientCheck:" + this.performClientCheck + + ", ClientCheckDone:" + this.clientSideChecked; + } + + /** + * Gets the preferred content type. + * + * @return the content type. + */ + public String getContentType() { + return contentType; + } + + /** + * Checks if this type supports also given browser. + * + * @param browser + * the browser type. + * @return true if this type matches the given browser. + */ + public boolean supports(String browser) { + return this.getBrowserApplication().indexOf(browser) >= 0; + } + + /** + * Checks if this type supports given markup language version. + * + * @param html + * the markup language version. + * @return true if this type supports the given markup version,otherwise false. + */ + public boolean supports(MarkupVersion html) { + return this.getMarkupVersion().supports(html); + } + + /** + * Checks if this type supports given javascript version. + * + * @param js + * the javascript version to check for. + * @return true if this type supports the given javascript version. + */ + public boolean supports(JavaScriptVersion js) { + return this.getJavaScriptVersion().supports(js); + } + + /** + * Parses HTML version from string. + * + * @param html + * @return HTMLVersion instance. + */ + private MarkupVersion doParseHTMLVersion(String html) { + for (int i = 0; i < MARKUP_VERSIONS.length; i++) { + if (MARKUP_VERSIONS[i].name.equals(html)) + return MARKUP_VERSIONS[i]; + } + return MARKUP_UNKNOWN; + } + + /** + * Parses JavaScript version from string. + * + * @param js + * the javascript version to check for. + * @return HTMLVersion instance. + */ + private JavaScriptVersion doParseJavaScriptVersion(String js) { + for (int i = 0; i < JAVASCRIPT_VERSIONS.length; i++) { + if (JAVASCRIPT_VERSIONS[i].name.toLowerCase().startsWith( + js.toLowerCase())) + return JAVASCRIPT_VERSIONS[i]; + } + return JAVASCRIPT_NONE; + } + + /** + * Parses HTML version from string. + * + * @param html + * @return the HTMLVersion instance. + */ + public static MarkupVersion parseHTMLVersion(String html) { + return DEFAULT.doParseHTMLVersion(html); + } + + /** + * Parse JavaScript version from string. + * + * @param js + * the javascript version to check for. + * @return the HTMLVersion instance. + */ + public static JavaScriptVersion parseJavaScriptVersion(String js) { + return DEFAULT.doParseJavaScriptVersion(js); + } + + /** + * Gets the client side cheked property. Certain terminal features can only + * be detected at client side. This property indicates if the client side + * detections have been performed for this type. + * + * @return true if client has sent information about its + * properties. Default is false. + */ + public boolean isClientSideChecked() { + return this.clientSideChecked; + } + + /** + * Sets the client side checked property. Certain terminal features can only + * be detected at client side. This property indicates if the client side + * detections have been performed for this type. + * + * @param value + * true if client has sent information about its properties, + * false otherweise. + */ + public void setClientSideChecked(boolean value) { + this.clientSideChecked = value; + } + + /** + * Should the client features be checked using remote scripts. Should the + * client side terminal feature check be performed. + * + * @return true if client side checking should be performed + * for this terminal type. Default is false. + */ + public boolean performClientCheck() { + return this.performClientCheck; + } + + /** + * Should the client features be checked using remote scripts. + * + * @param value + * @return true if client side checking should be performed + * for this terminal type. Default false. + */ + public void performClientCheck(boolean value) { + this.performClientCheck = value; + } + + /** + * Checks if web browser supports Java. + * + * @return true if the browser supports java otherwise false. + */ + public boolean isJavaEnabled() { + return javaEnabled; + } + + /** + * Returns the locales supported by the web browser. + * + * @return the Collection. + */ + public Collection getLocales() { + return locales; + } + + /** + * Sets the browser application. This corresponds to User-Agent HTTP header. + * + * @param browserApplication + * the browserApplication to set. + */ + public void setBrowserApplication(String browserApplication) { + this.browserApplication = browserApplication; + } + + /** + * Sets the default content type. Default is text/html + * + * @param contentType + * the contentType to set. + */ + public void setContentType(String contentType) { + this.contentType = contentType; + } + + /** + * Sets the java enabled property. + * + * @param javaEnabled + * the javaEnabled to set. + */ + public void setJavaEnabled(boolean javaEnabled) { + this.javaEnabled = javaEnabled; + } + + /** + * Sets the JavaScript version. + * + * @param javaScriptVersion + * the JavaScript version to set. + */ + public void setJavaScriptVersion(JavaScriptVersion javaScriptVersion) { + this.javaScriptVersion = javaScriptVersion; + } + + /** + * Sets the markup language version. + * + * @param markupVersion + * the markup language version to set. + */ + public void setMarkupVersion(MarkupVersion markupVersion) { + this.markupVersion = markupVersion; + } + + /** + * Sets the screen height. + * + * @param screenHeight + * the screen height to set in pixels. + */ + public void setScreenHeight(int screenHeight) { + this.screenHeight = screenHeight; + } + + /** + * Sets the screen width. + * + * @param screenWidth + * the screenWidth to set in pixels. + */ + public void setScreenWidth(int screenWidth) { + this.screenWidth = screenWidth; + } + + /* + * Consts defining the supported markup language versions @author IT Mill + * Ltd. + * + * @version @VERSION@ + * @since 3.0 + */ + public class MarkupVersion { + private String name; + + private int order; + + /** + * Returns true if and only if the argument is not + * null and is a Boolean object that represents the same + * boolean value as this object. + * + * @param obj + * the object to compare with. + * @see java.lang.Object#equals(Object) + */ + public boolean equals(Object obj) { + if (obj != null && obj instanceof MarkupVersion) + return name.equals(((MarkupVersion) obj).name); + return false; + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + return name; + } + + /** + * + * @param name + * @param order + */ + private MarkupVersion(String name, int order) { + this.name = name; + this.order = order; + } + + /** + * Checks the compability with other HTML version. + * + * @param other + * the HTML version. + * @return true if this is compatible with the other, + * otherwise false. + */ + public boolean supports(MarkupVersion other) { + return (this.order >= other.order); + } + + } + + public static final MarkupVersion MARKUP_UNKNOWN = DEFAULT.new MarkupVersion( + "HTML unknown", 0); + + public static final MarkupVersion MARKUP_HTML_2_0 = DEFAULT.new MarkupVersion( + "HTML 2.0", 20); + + public static final MarkupVersion MARKUP_HTML_3_2 = DEFAULT.new MarkupVersion( + "HTML 3.2", 32); + + public static final MarkupVersion MARKUP_HTML_4_0 = DEFAULT.new MarkupVersion( + "HTML 4.0", 40); + + public static final MarkupVersion MARKUP_XHTML_1_0 = DEFAULT.new MarkupVersion( + "XHTML 1.0", 110); + + public static final MarkupVersion MARKUP_XHTML_2_0 = DEFAULT.new MarkupVersion( + "XHTML 2.0", 120); + + public static final MarkupVersion MARKUP_WML_1_0 = DEFAULT.new MarkupVersion( + "WML 1.0", 10); + + public static final MarkupVersion MARKUP_WML_1_1 = DEFAULT.new MarkupVersion( + "WML 1.1", 11); + + public static final MarkupVersion MARKUP_WML_1_2 = DEFAULT.new MarkupVersion( + "WML 1.2", 12); + + public static final MarkupVersion[] MARKUP_VERSIONS = new MarkupVersion[] { + MARKUP_UNKNOWN, MARKUP_HTML_2_0, MARKUP_HTML_3_2, MARKUP_HTML_4_0, + MARKUP_XHTML_1_0, MARKUP_XHTML_2_0, MARKUP_WML_1_0, MARKUP_WML_1_1, + MARKUP_WML_1_2 }; + + /* + * Consts defining the supported JavaScript versions @author IT Mill Ltd. + * + * @version @VERSION@ + * @since 3.0 + */ + public class JavaScriptVersion { + private String name; + + private int order; + + /** + * @see java.lang.Object#equals(Object) + */ + public boolean equals(Object obj) { + if (obj != null && obj instanceof JavaScriptVersion) + return name.equals(((JavaScriptVersion) obj).name); + return false; + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + return name; + } + + /** + * + * @param name + * @param order + */ + private JavaScriptVersion(String name, int order) { + this.name = name; + this.order = order; + } + + /** + * Checks the compability with other JavaScript version. Use this like: + * boolean isEcma = someVersion.supports(ECMA_262); + * + * @param other + * the java script version. + * @return true if this supports the other, otherwise + * false. + */ + public boolean supports(JavaScriptVersion other) { + + // ECMA-262 support compare + if (other.equals(ECMA_262)) { + + // JScript over 5.0 support ECMA-262 + if (this.order >= 100) { + return (this.order >= JSCRIPT_5_0.order); + } else { + return (this.order >= JAVASCRIPT_1_3.order); + } + } + + // JavaScript version compare + else if (this.order < 100 && other.order < 100) { + return (this.order >= other.order); + } + + // JScript version compare + else if (this.order >= 100 && other.order >= 100) { + return (this.order >= other.order); + } + + return false; + + } + + } + + public static final JavaScriptVersion JAVASCRIPT_UNCHECKED = DEFAULT.new JavaScriptVersion( + "JavaScript unchecked", -1); + + public static final JavaScriptVersion JAVASCRIPT_NONE = DEFAULT.new JavaScriptVersion( + "JavaScript none", -1); + + public static final JavaScriptVersion JAVASCRIPT_1_0 = DEFAULT.new JavaScriptVersion( + "JavaScript 1.0", 10); + + public static final JavaScriptVersion JAVASCRIPT_1_1 = DEFAULT.new JavaScriptVersion( + "JavaScript 1.1", 11); + + public static final JavaScriptVersion JAVASCRIPT_1_2 = DEFAULT.new JavaScriptVersion( + "JavaScript 1.2", 12); + + public static final JavaScriptVersion JAVASCRIPT_1_3 = DEFAULT.new JavaScriptVersion( + "JavaScript 1.3", 13); + + public static final JavaScriptVersion JAVASCRIPT_1_4 = DEFAULT.new JavaScriptVersion( + "JavaScript 1.4", 14); + + public static final JavaScriptVersion JAVASCRIPT_1_5 = DEFAULT.new JavaScriptVersion( + "JavaScript 1.5", 15); + + public static final JavaScriptVersion JSCRIPT_1_0 = DEFAULT.new JavaScriptVersion( + "JScript 1.0", 110); + + public static final JavaScriptVersion JSCRIPT_3_0 = DEFAULT.new JavaScriptVersion( + "JScript 3.0", 130); + + public static final JavaScriptVersion JSCRIPT_4_0 = DEFAULT.new JavaScriptVersion( + "JScript 4.0", 140); + + public static final JavaScriptVersion JSCRIPT_5_0 = DEFAULT.new JavaScriptVersion( + "JScript 5.0", 150); + + public static final JavaScriptVersion JSCRIPT_5_1 = DEFAULT.new JavaScriptVersion( + "JScript 5.1", 151); + + public static final JavaScriptVersion JSCRIPT_5_5 = DEFAULT.new JavaScriptVersion( + "JScript 5.5", 155); + + public static final JavaScriptVersion JSCRIPT_5_6 = DEFAULT.new JavaScriptVersion( + "JScript 5.6", 156); + + public static final JavaScriptVersion JSCRIPT_5_7 = DEFAULT.new JavaScriptVersion( + "JScript 5.7", 157); + + public static final JavaScriptVersion ECMA_262 = DEFAULT.new JavaScriptVersion( + "ECMA-262", 262); + + public static final JavaScriptVersion[] JAVASCRIPT_VERSIONS = new JavaScriptVersion[] { + JAVASCRIPT_UNCHECKED, JAVASCRIPT_NONE, JAVASCRIPT_1_0, + JAVASCRIPT_1_1, JAVASCRIPT_1_2, JAVASCRIPT_1_3, JAVASCRIPT_1_4, + JAVASCRIPT_1_5, JSCRIPT_1_0, JSCRIPT_3_0, JSCRIPT_4_0, JSCRIPT_5_0, + JSCRIPT_5_1, JSCRIPT_5_5, JSCRIPT_5_6, JSCRIPT_5_7, ECMA_262 }; + +} diff --git a/src/com/itmill/toolkit/terminal/gwt/server/WebBrowserProbe.java b/src/com/itmill/toolkit/terminal/gwt/server/WebBrowserProbe.java new file mode 100644 index 0000000000..d6ca56c1b8 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/server/WebBrowserProbe.java @@ -0,0 +1,349 @@ +/* ************************************************************************* + + 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.util.Collection; +import java.util.Enumeration; +import java.util.Map; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +/** + * The WebBrowserProbe uses JavaScript to determine the + * capabilities of the client browser. + * + * @author IT Mill Ltd. + * @version + * @VERSION@ + * @since 3.0 + */ +public class WebBrowserProbe { + + private static final String WA_NOSCRIPT = "WA_NOSCRIPT"; + + private static final String CLIENT_TYPE = "wa_browser"; + + /** + * Returns the terminal type from the given session. + * + * @param session + * the HTTP session. + * @return WebBrowser instance for the given session. + */ + public static WebBrowser getTerminalType(HttpSession session) { + if (session != null) + return (WebBrowser) session.getAttribute(CLIENT_TYPE); + return null; + } + + /** + * Sets the terminal type for the given session. + * + * @param session + * the HTTP session. + * @param terminal + * the web browser. + * @return WebBrowser instance for the given session. + */ + public static void setTerminalType(HttpSession session, WebBrowser terminal) { + if (session != null) + session.setAttribute(CLIENT_TYPE, terminal); + } + + /** + * Handles the client checking. + * + * @param request + * the HTTP request to process. + * @param parameters + * the Parameters to be used as defaults. + * @return true if response should include a probe + * script,otherwise false. + * @throws ServletException + * if an exception has occurred that interferes with the + * servlet's normal operation. + */ + public static boolean handleProbeRequest(HttpServletRequest request, + Map parameters) throws ServletException { + + HttpSession s = request.getSession(); + WebBrowser browser = getTerminalType(s); + if (browser != null) { + + // Check if no-script was requested + if (parameters.containsKey(WA_NOSCRIPT)) { + String val = ((String[]) parameters.get(WA_NOSCRIPT))[0]; + if (val != null && "1".equals(val)) { + browser.setJavaScriptVersion(WebBrowser.JAVASCRIPT_NONE); + browser.setClientSideChecked(true); + } else { + // Recheck + browser.setClientSideChecked(false); + } + } + + // If client is alredy checked disable further checking + if (browser.isClientSideChecked()) + return false; + + } + + // Creates new type based on client parameters + browser = probe(browser, request, parameters); + setTerminalType(s, browser); + + // Sets client as checked if parameters were found + if (parameters.containsKey("wa_clientprobe")) { + String val = ((String[]) parameters.get("wa_clientprobe"))[0]; + browser.setClientSideChecked(val != null && "1".equals(val)); + } + + // Include probe script if requested and not alredy probed + return browser.performClientCheck() && !browser.isClientSideChecked(); + + } + + /** + * Determines versions based on user agent string. + * + * @param agent + * the HTTP User-Agent request header. + * @return new WebBrowser instance initialized based on agent features. + */ + public static WebBrowser probe(String agent) { + WebBrowser res = new WebBrowser(); + if (agent == null) + return res; + + // Set the agent string + res.setBrowserApplication(agent); + + // Konqueror + if (agent.indexOf("Konqueror") >= 0) { + res.setJavaScriptVersion(WebBrowser.JSCRIPT_5_6); + res.setJavaEnabled(true); + res.setFrameSupport(true); + } + + // Opera + else if (agent.indexOf("Opera 3.") >= 0) { + res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_1_3); + res.setJavaEnabled(false); + res.setFrameSupport(true); + } else if (agent.indexOf("Opera") >= 0) { + res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_1_3); + res.setJavaEnabled(true); + res.setFrameSupport(true); + res.setMarkupVersion(WebBrowser.MARKUP_HTML_4_0); + if (agent.indexOf("Opera/9") >= 0) + res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_1_5); + } + + // OmniWeb + else if (agent.indexOf("OmniWeb") >= 0) { + res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_1_3); + res.setJavaEnabled(true); + res.setFrameSupport(true); + } + + // Mosaic + else if (agent.indexOf("Mosaic") >= 0) { + res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_1_3); + res.setJavaEnabled(true); + res.setFrameSupport(true); + res.setMarkupVersion(WebBrowser.MARKUP_HTML_2_0); + } + + // Lynx + else if (agent.indexOf("Lynx") >= 0) { + res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_NONE); + res.setJavaEnabled(false); + res.setFrameSupport(true); + } + + // Microsoft Browsers + // See Microsoft documentation for details: + // http://msdn.microsoft.com/library/default.asp?url=/library/ + // en-us/script56/html/js56jsoriversioninformation.asp + else if (agent.indexOf("MSIE 7.") >= 0) { + res.setJavaScriptVersion(WebBrowser.JSCRIPT_5_7); + res.setJavaEnabled(true); + res.setFrameSupport(true); + res.setMarkupVersion(WebBrowser.MARKUP_HTML_4_0); + } else if (agent.indexOf("MSIE 6.") >= 0) { + res.setJavaScriptVersion(WebBrowser.JSCRIPT_5_6); + res.setJavaEnabled(true); + res.setFrameSupport(true); + res.setMarkupVersion(WebBrowser.MARKUP_HTML_4_0); + } else if (agent.indexOf("MSIE 5.5") >= 0) { + res.setJavaScriptVersion(WebBrowser.JSCRIPT_5_5); + res.setJavaEnabled(true); + res.setFrameSupport(true); + res.setMarkupVersion(WebBrowser.MARKUP_HTML_4_0); + } else if (agent.indexOf("MSIE 5.") >= 0) { + res.setJavaScriptVersion(WebBrowser.JSCRIPT_5_0); + res.setJavaEnabled(true); + res.setFrameSupport(true); + res.setMarkupVersion(WebBrowser.MARKUP_HTML_4_0); + } else if (agent.indexOf("MSIE 4.") >= 0) { + res.setJavaScriptVersion(WebBrowser.JSCRIPT_3_0); + res.setJavaEnabled(true); + res.setFrameSupport(true); + res.setMarkupVersion(WebBrowser.MARKUP_HTML_4_0); + } else if (agent.indexOf("MSIE 3.") >= 0) { + res.setJavaScriptVersion(WebBrowser.JSCRIPT_3_0); + res.setJavaEnabled(true); + res.setFrameSupport(true); + } else if (agent.indexOf("MSIE 2.") >= 0) { + res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_NONE); + res.setJavaEnabled(false); + if (agent.indexOf("Mac") >= 0) { + res.setFrameSupport(true); + } else { + res.setFrameSupport(false); + } + } + + // Netscape browsers + else if (agent.indexOf("Netscape6") >= 0) { + res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_1_5); + res.setJavaEnabled(true); + res.setFrameSupport(true); + res.setMarkupVersion(WebBrowser.MARKUP_HTML_4_0); + } else if ((agent.indexOf("Mozilla/4.06") >= 0) + || (agent.indexOf("Mozilla/4.7") >= 0)) { + res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_1_3); + res.setJavaEnabled(true); + res.setFrameSupport(true); + res.setMarkupVersion(WebBrowser.MARKUP_HTML_4_0); + } else if (agent.indexOf("Mozilla/4.") >= 0) { + res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_1_2); + res.setJavaEnabled(true); + res.setFrameSupport(true); + } else if (agent.indexOf("Mozilla/3.") >= 0) { + res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_1_1); + res.setJavaEnabled(true); + res.setFrameSupport(true); + } else if (agent.indexOf("Mozilla/2.") >= 0) { + res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_1_0); + res.setJavaEnabled(true); + res.setFrameSupport(true); + } + + // Mozilla Open-Source Browsers + else if (agent.indexOf("Mozilla/5.") >= 0) { + res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_1_5); + res.setJavaEnabled(true); + res.setFrameSupport(true); + res.setMarkupVersion(WebBrowser.MARKUP_HTML_4_0); + } + + // Unknown browser + else { + res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_UNCHECKED); + res.setJavaEnabled(false); + res.setMarkupVersion(WebBrowser.MARKUP_UNKNOWN); + res.setFrameSupport(false); + } + + return res; + } + + /** + * Creates new instance of WebBrowser by initializing the values based on + * user request. + * + * @param browser + * the browser to be updated. If null a new instance is created. + * @param request + * the Request to be used as defaults. + * @param params + * the Parameters to be used as defaults. + * @return new WebBrowser instance initialized based on request parameters. + */ + public static WebBrowser probe(WebBrowser browser, + HttpServletRequest request, Map params) { + + // Initialize defaults based on client features + WebBrowser res = browser; + if (res == null) { + res = probe(request.getHeader("User-Agent")); + } + + // Client locales + Collection locales = res.getLocales(); + locales.clear(); + for (Enumeration e = request.getLocales(); e.hasMoreElements();) { + locales.add(e.nextElement()); + } + + // Javascript version + if (params.containsKey("wa_jsversion")) { + String val = ((String[]) params.get("wa_jsversion"))[0]; + if (val != null) { + res + .setJavaScriptVersion(WebBrowser + .parseJavaScriptVersion(val)); + } + } + // Java support + if (params.containsKey("wa_javaenabled")) { + String val = ((String[]) params.get("wa_javaenabled"))[0]; + if (val != null) { + res.setJavaEnabled(Boolean.valueOf(val).booleanValue()); + } + } + // Screen width + if (params.containsKey("wa_screenwidth")) { + String val = ((String[]) params.get("wa_screenwidth"))[0]; + if (val != null) { + try { + res.setScreenWidth(Integer.parseInt(val)); + } catch (NumberFormatException e) { + res.setScreenWidth(-1); + } + } + } + // Screen height + if (params.containsKey("wa_screenheight")) { + String val = ((String[]) params.get("wa_screenheight"))[0]; + if (val != null) { + try { + res.setScreenHeight(Integer.parseInt(val)); + } catch (NumberFormatException e) { + res.setScreenHeight(-1); + } + } + } + + return res; + } +} \ No newline at end of file diff --git a/src/com/itmill/toolkit/terminal/web/AjaxApplicationManager.java b/src/com/itmill/toolkit/terminal/web/AjaxApplicationManager.java deleted file mode 100644 index 0b7d7f361e..0000000000 --- a/src/com/itmill/toolkit/terminal/web/AjaxApplicationManager.java +++ /dev/null @@ -1,940 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import java.io.BufferedWriter; -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.Collections; -import java.util.Comparator; -import java.util.GregorianCalendar; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -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.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.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 3.1 - */ -public class AjaxApplicationManager 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(); - - // TODO THIS TEMPORARY HACK IS ONLY HERE TO MAKE GWT DEVEL EASIER - static WeakHashMap paintableIdMap = new WeakHashMap(); - - private int idSequence = 0; - - private Application application; - - private Set removedWindows = new HashSet(); - - private PaintTarget paintTarget; - - private List locales; - - private int pendingLocalesIndex; - - public AjaxApplicationManager(Application application) { - this.application = application; - requireLocale(application.getLocale().toString()); - } - - /** - * - * @return - */ - private AjaxVariableMap getVariableMap() { - AjaxVariableMap vm = (AjaxVariableMap) applicationToVariableMapMap - .get(application); - if (vm == null) { - vm = new AjaxVariableMap(); - applicationToVariableMapMap.put(application, vm); - } - return vm; - } - - /** - * - * - */ - 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, ThemeSource themeSource) throws IOException { - handleUidlRequest(request, - response, themeSource, false); - - } - - - /** - * - * @param request - * the HTTP Request. - * @param response - * the HTTP Response. - * @throws IOException - * if the writing failed due to input/output error. - */ - public void handleUidlRequest(HttpServletRequest request, - HttpServletResponse response, ThemeSource themeSource, boolean isJson) - 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"))); - - 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 = getVariableMap().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 AjaxJsonPaintTarget(getVariableMap(), - 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 - ((AjaxPaintTarget) paintTarget).setTrackPaints(true); - p.paint(paintTarget); - - // If no paints add attribute empty - if (((AjaxPaintTarget) paintTarget).getNumberOfPaints() <= 0) { - paintTarget.addAttribute("visible", false); - } - paintTarget.endTag("change"); - paintablePainted(p); - } - } - - ((AjaxPaintTarget) paintTarget).close(); - outWriter.print("]"); // close changes - - - // Render the removed windows - // TODO refactor commented area to send some meta instructions to close window -// Set removed = new HashSet(getRemovedWindows()); -// if (removed.size() > 0) { -// for (Iterator i = removed.iterator(); i.hasNext();) { -// Window w = (Window) i.next(); -// paintTarget.startTag("change"); -// paintTarget.addAttribute("format", "uidl"); -// String pid = getPaintableId(w); -// paintTarget.addAttribute("pid", pid); -// paintTarget.addAttribute("windowname", w.getName()); -// paintTarget.addAttribute("visible", false); -// paintTarget.endTag("change"); -// removedWindowNotified(w); -// -// } -// } - - - - 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=((AjaxPaintTarget) paintTarget).getPreCachedResources().iterator(); i.hasNext();) { - String resource = (String) i.next(); - InputStream is = null; - try { - is = themeSource.getResource(themeName + "/" + resource); - } catch (ThemeSource.ThemeException 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("\"" + AjaxJsonPaintTarget.escapeJSON(layout.toString()) + "\""); - } - } - outWriter.print("}"); - - - /* ----------------------------- - * 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); - - 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("Application Internal Error"); - err.write("

" + e.toString() + "

\n");
-			e.printStackTrace(new PrintWriter(err));
-			err.write("\n
"); - err.close(); - } finally { - - } - - } - - /** - * 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 true if the request was handled and - * further processing should be suppressed, otherwise - * false. - * @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(""); - out.println(""); - } - - /** - * 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); - } - - 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); - } - - /** - * 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]); - } -} diff --git a/src/com/itmill/toolkit/terminal/web/AjaxHttpUploadStream.java b/src/com/itmill/toolkit/terminal/web/AjaxHttpUploadStream.java deleted file mode 100644 index d277bd16cd..0000000000 --- a/src/com/itmill/toolkit/terminal/web/AjaxHttpUploadStream.java +++ /dev/null @@ -1,114 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import java.io.InputStream; - -/** - * AjaxAdapter implementation of the UploadStream interface. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.1 - */ -public class AjaxHttpUploadStream implements - com.itmill.toolkit.terminal.UploadStream { - - /** - * Holds value of property variableName. - */ - private String streamName; - - private String contentName; - - private String contentType; - - /** - * Holds value of property variableValue. - */ - private InputStream stream; - - /** - * Creates a new instance of UploadStreamImpl. - * - * @param name - * the name of the stream. - * @param stream - * the input stream. - * @param contentName - * the name of the content. - * @param contentType - * the type of the content. - */ - public AjaxHttpUploadStream(String name, InputStream stream, - String contentName, String contentType) { - this.streamName = name; - this.stream = stream; - this.contentName = contentName; - this.contentType = contentType; - } - - /** - * Gets the name of the stream. - * - * @return the name of the stream. - */ - public String getStreamName() { - return this.streamName; - } - - /** - * Gets the input stream. - * - * @return the Input stream. - */ - public InputStream getStream() { - return this.stream; - } - - /** - * Gets the input stream content type. - * - * @return the content type of the input stream. - */ - public String getContentType() { - return this.contentType; - } - - /** - * Gets the stream content name. Stream content name usually differs from - * the actual stream name. It is used to identify the content of the stream. - * - * @return the Name of the stream content. - */ - public String getContentName() { - return this.contentName; - } -} diff --git a/src/com/itmill/toolkit/terminal/web/AjaxJsonPaintTarget.java b/src/com/itmill/toolkit/terminal/web/AjaxJsonPaintTarget.java deleted file mode 100644 index 882dd8f140..0000000000 --- a/src/com/itmill/toolkit/terminal/web/AjaxJsonPaintTarget.java +++ /dev/null @@ -1,980 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import com.itmill.toolkit.Application; -import com.itmill.toolkit.terminal.ApplicationResource; -import com.itmill.toolkit.terminal.ExternalResource; -import com.itmill.toolkit.terminal.PaintException; -import com.itmill.toolkit.terminal.PaintTarget; -import com.itmill.toolkit.terminal.Paintable; -import com.itmill.toolkit.terminal.Resource; -import com.itmill.toolkit.terminal.ThemeResource; -import com.itmill.toolkit.terminal.UploadStream; -import com.itmill.toolkit.terminal.VariableOwner; - -import java.io.BufferedWriter; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.io.UnsupportedEncodingException; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.Set; -import java.util.Stack; -import java.util.Vector; - -/** - * User Interface Description Language Target. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.1 - */ -public class AjaxJsonPaintTarget implements PaintTarget, AjaxPaintTarget { - - /* Document type declarations */ - - private final static String UIDL_ARG_NAME = "name"; - - private final static String UIDL_ARG_VALUE = "value"; - - private final static String UIDL_ARG_ID = "id"; - - private Stack mOpenTags; - - private Stack openJsonTags; - - private boolean mTagArgumentListOpen; - - private PrintWriter uidlBuffer; - - private AjaxVariableMap variableMap; - - private boolean closed = false; - - private AjaxApplicationManager manager; - - private boolean trackPaints = false; - - private int numberOfPaints = 0; - - private int changes = 0; - - Set preCachedResources = new HashSet(); - - private boolean customLayoutArgumentsOpen = false; - - private JsonTag tag; - - /** - * Creates a new XMLPrintWriter, without automatic line flushing. - * - * @param variableMap - * @param manager - * @param outWriter - * A character-output stream. - * @throws PaintException - * if the paint operation failed. - */ - public AjaxJsonPaintTarget(AjaxVariableMap variableMap, - AjaxApplicationManager manager, PrintWriter outWriter) - throws PaintException { - - this.manager = manager; - // Sets the variable map - this.variableMap = variableMap; - - // Sets the target for UIDL writing - this.uidlBuffer = outWriter; - - // Initialize tag-writing - mOpenTags = new Stack(); - openJsonTags = new Stack(); - mTagArgumentListOpen = false; - - // Adds document declaration - - // Adds UIDL start tag and its attributes - } - - public void startTag(String tagName) throws PaintException { - startTag(tagName, false); - } - - /** - * Prints the element start tag. - * - *
-	 *   Todo:
-	 *    Checking of input values
-	 *    
-	 * 
- * - * @param tagName - * the name of the start tag. - * @throws PaintException - * if the paint operation failed. - * - */ - public void startTag(String tagName, boolean isChildNode) - throws PaintException { - // In case of null data output nothing: - if (tagName == null) - throw new NullPointerException(); - - // Increments paint tracker - if (this.isTrackPaints()) { - this.numberOfPaints++; - } - - // Ensures that the target is open - if (this.closed) - throw new PaintException( - "Attempted to write to a closed PaintTarget."); - - if (tag != null) { - openJsonTags.push(tag); - } - // Checks tagName and attributes here - mOpenTags.push(tagName); - - tag = new JsonTag(tagName); - - mTagArgumentListOpen = true; - - customLayoutArgumentsOpen = "customlayout".equals(tagName); - } - - /** - * Prints the element end tag. - * - * If the parent tag is closed before every child tag is closed an - * PaintException is raised. - * - * @param tag - * the name of the end tag. - * @throws Paintexception - * if the paint operation failed. - */ - public void endTag(String tagName) throws PaintException { - // In case of null data output nothing: - if (tagName == null) - throw new NullPointerException(); - - // Ensure that the target is open - if (this.closed) - throw new PaintException( - "Attempted to write to a closed PaintTarget."); - - if (openJsonTags.size() > 0) { - JsonTag parent = (JsonTag) openJsonTags.pop(); - - String lastTag = ""; - - lastTag = (String) mOpenTags.pop(); - if (!tagName.equalsIgnoreCase(lastTag)) - throw new PaintException("Invalid UIDL: wrong ending tag: '" - + tagName + "' expected: '" + lastTag + "'."); - - parent.addData(tag.getJSON()); - - tag = parent; - } else { - changes++; - this.uidlBuffer.print(((changes > 1) ? "," : "") + tag.getJSON()); - tag = null; - } - } - - /** - * Substitutes the XML sensitive characters with predefined XML entities. - * - * @param xml - * the String to be substituted. - * @return A new string instance where all occurrences of XML sensitive - * characters are substituted with entities. - */ - static public String escapeXML(String xml) { - if (xml == null || xml.length() <= 0) - return ""; - return escapeXML(new StringBuffer(xml)).toString(); - } - - /** - * Substitutes the XML sensitive characters with predefined XML entities. - * - * @param xml - * the String to be substituted. - * @return A new StringBuffer instance where all occurrences of XML - * sensitive characters are substituted with entities. - * - */ - static public StringBuffer escapeXML(StringBuffer xml) { - if (xml == null || xml.length() <= 0) - return new StringBuffer(""); - - StringBuffer result = new StringBuffer(xml.length() * 2); - - for (int i = 0; i < xml.length(); i++) { - char c = xml.charAt(i); - String s = toXmlChar(c); - if (s != null) { - result.append(s); - } else { - result.append(c); - } - } - return result; - } - - static public String escapeJSON(String s) { - if (s == null) - return ""; - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < s.length(); i++) { - char ch = s.charAt(i); - switch (ch) { - case '"': - sb.append("\\\""); - break; - case '\\': - sb.append("\\\\"); - break; - case '\b': - sb.append("\\b"); - break; - case '\f': - sb.append("\\f"); - break; - case '\n': - sb.append("\\n"); - break; - case '\r': - sb.append("\\r"); - break; - case '\t': - sb.append("\\t"); - break; - case '/': - sb.append("\\/"); - break; - default: - if (ch >= '\u0000' && ch <= '\u001F') { - String ss = Integer.toHexString(ch); - sb.append("\\u"); - for (int k = 0; k < 4 - ss.length(); k++) { - sb.append('0'); - } - sb.append(ss.toUpperCase()); - } else { - sb.append(ch); - } - } - } - return sb.toString(); - } - - /** - * Substitutes a XML sensitive character with predefined XML entity. - * - * @param c - * the Character to be replaced with an entity. - * @return String of the entity or null if character is not to be replaced - * with an entity. - */ - private static String toXmlChar(char c) { - switch (c) { - case '&': - return "&"; // & => & - case '>': - return ">"; // > => > - case '<': - return "<"; // < => < - case '"': - return """; // " => " - case '\'': - return "'"; // ' => ' - default: - return null; - } - } - - /** - * Prints XML-escaped text. - * - * @param str - * @throws PaintException - * if the paint operation failed. - * - */ - public void addText(String str) throws PaintException { - tag.addData("\"" + escapeJSON(str) + "\""); - } - - /** - * Adds a boolean attribute to component. Atributes must be added before any - * content is written. - * - * @param name - * the Attribute name. - * @param value - * the Attribute value. - * @throws PaintException - * if the paint operation failed. - */ - public void addAttribute(String name, boolean value) throws PaintException { - tag.addAttribute("\"" + name + "\":" + (value ? "true" : "false")); - } - - /** - * Adds a resource attribute to component. Atributes must be added before - * any content is written. - * - * @param name - * the Attribute name. - * @param value - * the Attribute value. - * - * @throws PaintException - * if the paint operation failed. - */ - public void addAttribute(String name, Resource value) throws PaintException { - - if (value instanceof ExternalResource) { - addAttribute(name, ((ExternalResource) value).getURL()); - - } else if (value instanceof ApplicationResource) { - ApplicationResource r = (ApplicationResource) value; - Application a = r.getApplication(); - if (a == null) - throw new PaintException( - "Application not specified for resorce " - + value.getClass().getName()); - String uri = a.getURL().getPath(); - if (uri.charAt(uri.length() - 1) != '/') - uri += "/"; - uri += a.getRelativeLocation(r); - addAttribute(name, uri); - - } else if (value instanceof ThemeResource) { - String uri = "theme://" + ((ThemeResource) value).getResourceId(); - addAttribute(name, uri); - } else - throw new PaintException("Ajax adapter does not " - + "support resources of type: " - + value.getClass().getName()); - - } - - /** - * Adds a integer attribute to component. Atributes must be added before any - * content is written. - * - * @param name - * the Attribute name. - * @param value - * the Attribute value. - * - * @throws PaintException - * if the paint operation failed. - */ - public void addAttribute(String name, int value) throws PaintException { - tag.addAttribute("\"" + name + "\":" + String.valueOf(value)); - } - - /** - * Adds a long attribute to component. Atributes must be added before any - * content is written. - * - * @param name - * the Attribute name. - * @param value - * the Attribute value. - * - * @throws PaintException - * if the paint operation failed. - */ - public void addAttribute(String name, long value) throws PaintException { - tag.addAttribute("\"" + name + "\":" + String.valueOf(value)); - } - - /** - * Adds a string attribute to component. Atributes must be added before any - * content is written. - * - * @param name - * the String attribute name. - * @param value - * the String attribute value. - * - * @throws PaintException - * if the paint operation failed. - */ - public void addAttribute(String name, String value) throws PaintException { - // In case of null data output nothing: - if ((value == null) || (name == null)) - throw new NullPointerException( - "Parameters must be non-null strings"); - - tag.addAttribute("\"" + name + "\": \"" + escapeJSON(value) + "\""); - - if (customLayoutArgumentsOpen && "style".equals(name)) - getPreCachedResources().add("layout/" + value + ".html"); - - if(name.equals("locale")) - manager.requireLocale(value); - - } - - public void addAttribute(String name, Object[] values) { - // In case of null data output nothing: - if ((values == null) || (name == null)) - throw new NullPointerException( - "Parameters must be non-null strings"); - StringBuffer buf = new StringBuffer(); - buf.append("\"" + name + "\":["); - for (int i = 0; i < values.length; i++) { - if (i > 0) - buf.append(","); - buf.append("\""); - buf.append(escapeJSON(values[i].toString())); - buf.append("\""); - } - buf.append("]"); - tag.addAttribute(buf.toString()); - } - - /** - * Adds a string type variable. - * - * @param owner - * the Listener for variable changes. - * @param name - * the Variable name. - * @param value - * the Variable initial value. - * - * @throws PaintException - * if the paint operation failed. - */ - public void addVariable(VariableOwner owner, String name, String value) - throws PaintException { - tag.addVariable(new StringVariable(owner, name, value)); - } - - /** - * Adds a int type variable. - * - * @param owner - * the Listener for variable changes. - * @param name - * the Variable name. - * @param value - * the Variable initial value. - * - * @throws PaintException - * if the paint operation failed. - */ - public void addVariable(VariableOwner owner, String name, int value) - throws PaintException { - tag.addVariable(new IntVariable(owner, name, value)); - } - - /** - * Adds a boolean type variable. - * - * @param owner - * the Listener for variable changes. - * @param name - * the Variable name. - * @param value - * the Variable initial value. - * - * @throws PaintException - * if the paint operation failed. - */ - public void addVariable(VariableOwner owner, String name, boolean value) - throws PaintException { - tag.addVariable(new BooleanVariable(owner, name, value)); - } - - /** - * Adds a string array type variable. - * - * @param owner - * the Listener for variable changes. - * @param name - * the Variable name. - * @param value - * the Variable initial value. - * - * @throws PaintException - * if the paint operation failed. - */ - public void addVariable(VariableOwner owner, String name, String[] value) - throws PaintException { - tag.addVariable(new ArrayVariable(owner, name, value)); - } - - /** - * Adds a upload stream type variable. - * - * TODO not converted for JSON - * - * @param owner - * the Listener for variable changes. - * @param name - * the Variable name. - * - * @throws PaintException - * if the paint operation failed. - */ - public void addUploadStreamVariable(VariableOwner owner, String name) - throws PaintException { - String code = variableMap.registerVariable(name, UploadStream.class, - null, owner); - startTag("uploadstream"); - addAttribute(UIDL_ARG_ID, code); - addAttribute(UIDL_ARG_NAME, name); - endTag("uploadstream"); - } - - /** - * Prints the single text section. - * - * Prints full text section. The section data is escaped - * - * @param sectionTagName - * the name of the tag. - * @param sectionData - * the section data to be printed. - * @throws PaintException - * if the paint operation failed. - */ - public void addSection(String sectionTagName, String sectionData) - throws PaintException { - tag.addData("{\"" + sectionTagName + "\":\"" + escapeJSON(sectionData) - + "\"}"); - } - - /** - * Adds XML directly to UIDL. - * - * @param xml - * the Xml to be added. - * @throws PaintException - * if the paint operation failed. - */ - public void addUIDL(String xml) throws PaintException { - - // Ensure that the target is open - if (this.closed) - throw new PaintException( - "Attempted to write to a closed PaintTarget."); - - // Make sure that the open start tag is closed before - // anything is written. - - // Escape and write what was given - if (xml != null) - tag.addData("\"" + escapeJSON(xml) + "\""); - - } - - /** - * Adds XML section with namespace. - * - * @param sectionTagName - * the name of the tag. - * @param sectionData - * the section data. - * @param namespace - * the namespace to be added. - * @throws PaintException - * if the paint operation failed. - * - * @see com.itmill.toolkit.terminal.PaintTarget#addXMLSection(String, - * String, String) - */ - public void addXMLSection(String sectionTagName, String sectionData, - String namespace) throws PaintException { - - // Ensure that the target is open - if (this.closed) - throw new PaintException( - "Attempted to write to a closed PaintTarget."); - - startTag(sectionTagName); - if (namespace != null) - addAttribute("xmlns", namespace); - mTagArgumentListOpen = false; - customLayoutArgumentsOpen = false; - - if (sectionData != null) - tag.addData("\"" + escapeJSON(sectionData) + "\""); - endTag(sectionTagName); - } - - /** - * Gets the UIDL already printed to stream. Paint target must be closed - * before the getUIDL can be called. - * - * @return the UIDL. - */ - public String getUIDL() { - if (this.closed) { - return uidlBuffer.toString(); - } - throw new IllegalStateException( - "Tried to read UIDL from open PaintTarget"); - } - - /** - * Closes the paint target. Paint target must be closed before the - * getUIDL can be called. Subsequent attempts to write to - * paint target. If the target was already closed, call to this function is - * ignored. will generate an exception. - * - * @throws PaintException - * if the paint operation failed. - */ - public void close() throws PaintException { - if (tag != null) - uidlBuffer.append(tag.getJSON()); - flush(); - this.closed = true; - } - - /** - * Method flush. - */ - private void flush() { - this.uidlBuffer.flush(); - } - - /** - * @see com.itmill.toolkit.terminal.PaintTarget#startTag(com.itmill.toolkit.terminal.Paintable, - * java.lang.String) - */ - public boolean startTag(Paintable paintable, String tagName) - throws PaintException { - startTag(tagName, true); - String id = manager.getPaintableId(paintable); - paintable.addListener(manager); - addAttribute("id", id); - return false; - } - - /** - * @see com.itmill.toolkit.terminal.PaintTarget#addCharacterData(java.lang.String) - */ - public void addCharacterData(String text) throws PaintException { - if (text != null) - tag.addData(text); - } - - /** - * - * @return - */ - public boolean isTrackPaints() { - return trackPaints; - } - - /** - * Gets the number of paints. - * - * @return the number of paints. - */ - public int getNumberOfPaints() { - return numberOfPaints; - } - - /** - * Sets the tracking to true or false. - * - * This also resets the number of paints. - * - * @param enabled - * is the tracking is enabled or not. - * @see #getNumberOfPaints() - */ - public void setTrackPaints(boolean enabled) { - this.trackPaints = enabled; - this.numberOfPaints = 0; - } - - /** - * This is basically a container for UI components variables, that will be - * added at the end of JSON object. - * - * @author mattitahvonen - * - */ - class JsonTag { - boolean firstField = false; - - Vector variables = new Vector(); - - Vector children = new Vector(); - - Vector attr = new Vector(); - - private HashMap childTagCounters = new HashMap(); - - StringBuffer data = new StringBuffer(); - - public boolean childrenArrayOpen = false; - - private boolean childNode = false; - - private boolean tagClosed = false; - - public JsonTag(String tagName) { - data.append("[\"" + tagName + "\""); - } - - private void closeTag() { - if (!tagClosed) { - data.append(attributesAsJsonObject()); - data.append(getData()); - // Writes the end (closing) tag - data.append("]"); - this.tagClosed = true; - } - } - - public String getJSON() { - if (!tagClosed) { - this.closeTag(); - } - return data.toString(); - } - - public void openChildrenArray() { - if (!childrenArrayOpen) { - // append("c : ["); - childrenArrayOpen = true; - // firstField = true; - } - } - - public void closeChildrenArray() { - // append("]"); - // firstField = false; - } - - public void setChildNode(boolean b) { - this.childNode = b; - } - - public boolean isChildNode() { - return childNode; - } - - public String startField() { - if (firstField) { - firstField = false; - return ""; - } else { - return ","; - } - } - - /** - * - * @param s - * json string, object or array - */ - public void addData(String s) { - children.add(s); - } - - public String getData() { - StringBuffer buf = new StringBuffer(); - Iterator it = children.iterator(); - while (it.hasNext()) { - buf.append(startField()); - buf.append(it.next()); - } - return buf.toString(); - } - - public void addAttribute(String jsonNode) { - attr.add(jsonNode); - } - - private String attributesAsJsonObject() { - StringBuffer buf = new StringBuffer(); - buf.append(startField()); - buf.append("{"); - for (Iterator iter = attr.iterator(); iter.hasNext();) { - String element = (String) iter.next(); - buf.append(element); - if (iter.hasNext()) - buf.append(","); - } - buf.append(tag.variablesAsJsonObject()); - buf.append("}"); - return buf.toString(); - } - - public void addVariable(Variable v) { - variables.add(v); - } - - private String variablesAsJsonObject() { - if (variables.size() == 0) - return ""; - StringBuffer buf = new StringBuffer(); - buf.append(startField()); - buf.append("\"v\":{"); - Iterator iter = variables.iterator(); - while (iter.hasNext()) { - Variable element = (Variable) iter.next(); - buf.append(element.getJsonPresentation()); - if (iter.hasNext()) - buf.append(","); - } - buf.append("}"); - return buf.toString(); - } - - class TagCounter { - int count; - - public TagCounter() { - count = 0; - } - - public void increment() { - count++; - } - - public String postfix(String s) { - if (count > 0) { - return s + count; - } - return s; - } - } - } - - abstract class Variable { - String code; - - String name; - - public abstract String getJsonPresentation(); - } - - class BooleanVariable extends Variable { - boolean value; - - public BooleanVariable(VariableOwner owner, String name, boolean v) { - value = v; - this.name = name; - code = variableMap.registerVariable(name, Boolean.class, - new Boolean(value), owner); - } - - public String getJsonPresentation() { - return "\"" + name + "\":" + (value == true ? "true" : "false"); - } - - } - - class StringVariable extends Variable { - String value; - - public StringVariable(VariableOwner owner, String name, String v) { - value = v; - this.name = name; - code = variableMap.registerVariable(name, String.class, value, - owner); - } - - public String getJsonPresentation() { - return "\"" + name + "\":\"" + value + "\""; - } - - } - - class IntVariable extends Variable { - int value; - - public IntVariable(VariableOwner owner, String name, int v) { - value = v; - this.name = name; - code = variableMap.registerVariable(name, Integer.class, - new Integer(value), owner); - } - - public String getJsonPresentation() { - return "\"" + name + "\":" + value; - } - } - - class ArrayVariable extends Variable { - String[] value; - - public ArrayVariable(VariableOwner owner, String name, String[] v) { - value = v; - this.name = name; - code = variableMap.registerVariable(name, String[].class, value, - owner); - } - - public String getJsonPresentation() { - String pres = "\"" + name + "\":["; - for (int i = 0; i < value.length;) { - pres += "\"" + value[i] + "\""; - i++; - if (i < value.length) - pres += ","; - } - pres += "]"; - return pres; - } - } - - public Set getPreCachedResources() { - return preCachedResources; - } - - public void setPreCachedResources(Set preCachedResources) { - // TODO Auto-generated method stub - - } - -} diff --git a/src/com/itmill/toolkit/terminal/web/AjaxPaintTarget.java b/src/com/itmill/toolkit/terminal/web/AjaxPaintTarget.java deleted file mode 100644 index 8db97e7dfb..0000000000 --- a/src/com/itmill/toolkit/terminal/web/AjaxPaintTarget.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.itmill.toolkit.terminal.web; - -import java.util.Set; - -import com.itmill.toolkit.terminal.PaintException; -import com.itmill.toolkit.terminal.Paintable; -import com.itmill.toolkit.terminal.Resource; -import com.itmill.toolkit.terminal.VariableOwner; - -public interface AjaxPaintTarget { - - /** - * Gets the UIDL already printed to stream. Paint target must be closed - * before the getUIDL can be called. - * - * @return the UIDL. - */ - public abstract String getUIDL(); - - /** - * Closes the paint target. Paint target must be closed before the - * getUIDL can be called. Subsequent attempts to write to - * paint target. If the target was already closed, call to this function is - * ignored. will generate an exception. - * - * @throws PaintException - * if the paint operation failed. - */ - public abstract void close() throws PaintException; - - /** - * - * @return - */ - public abstract boolean isTrackPaints(); - - /** - * Gets the number of paints. - * - * @return the number of paints. - */ - public abstract int getNumberOfPaints(); - - /** - * Sets the tracking to true or false. - * - * This also resets the number of paints. - * - * @param enabled - * is the tracking is enabled or not. - * @see #getNumberOfPaints() - */ - public abstract void setTrackPaints(boolean enabled); - - public abstract void setPreCachedResources(Set preCachedResources); - - public abstract Set getPreCachedResources() ; - -} \ No newline at end of file diff --git a/src/com/itmill/toolkit/terminal/web/AjaxVariableMap.java b/src/com/itmill/toolkit/terminal/web/AjaxVariableMap.java deleted file mode 100644 index 80980d8b69..0000000000 --- a/src/com/itmill/toolkit/terminal/web/AjaxVariableMap.java +++ /dev/null @@ -1,803 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import java.io.IOException; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.StringTokenizer; -import java.util.WeakHashMap; - -import javax.servlet.http.HttpServletRequest; - -import com.itmill.toolkit.terminal.SystemError; -import com.itmill.toolkit.terminal.Terminal; -import com.itmill.toolkit.terminal.UploadStream; -import com.itmill.toolkit.terminal.VariableOwner; - -/** - * Variable map for ajax applications. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.1 - */ -public class AjaxVariableMap { - - // Id <-> (Owner,Name) mapping - private Map idToNameMap = new HashMap(); - - private Map idToTypeMap = new HashMap(); - - private Map idToOwnerMap = new HashMap(); - - private Map idToValueMap = new HashMap(); - - private Map ownerToNameToIdMap = new WeakHashMap(); - - private Object mapLock = new Object(); - - // Id generator - private long lastId = 0; - - /** - * Converts the string to a supported class. - * - * @param type - * @param value - * @return - * @throws java.lang.ClassCastException - * if the code has attempted to cast an object to a subclass of - * which it is not an instance - */ - private static Object convert(Class type, String value) - throws java.lang.ClassCastException { - try { - - // Boolean typed variables - if (type.equals(Boolean.class)) - return new Boolean(!(value.equals("") || value.equals("false"))); - - // Integer typed variables - if (type.equals(Integer.class)) - return new Integer(value.trim()); - - // String typed variables - if (type.equals(String.class)) - return value; - - throw new ClassCastException("Unsupported type: " + type.getName()); - } catch (NumberFormatException e) { - return null; - } - } - - /** - * Registers a new variable. - * - * @param name - * the Variable name. - * @param type - * @param value - * @param owner - * the Listener for variable changes. - * @return id to assigned for this variable. - */ - public String registerVariable(String name, Class type, Object value, - VariableOwner owner) { - - // Checks that the type of the class is supported - if (!(type.equals(Boolean.class) || type.equals(Integer.class) - || type.equals(String.class) || type.equals(String[].class) || type - .equals(UploadStream.class))) - throw new SystemError("Unsupported variable type: " - + type.getClass()); - - synchronized (mapLock) { - - // Checks if the variable is already mapped - HashMap nameToIdMap = (HashMap) ownerToNameToIdMap.get(owner); - if (nameToIdMap == null) { - nameToIdMap = new HashMap(); - ownerToNameToIdMap.put(owner, nameToIdMap); - } - String id = (String) nameToIdMap.get(name); - - if (id == null) { - // Generates new id and register it - -// ---------- -// TODO This HACK is only included for testing GWT integration -//Original id = "v" + String.valueOf(++lastId); - Object pid = AjaxApplicationManager.paintableIdMap.get(owner); - id = pid + "_"+name; -// ---------- - - - nameToIdMap.put(name, id); - idToOwnerMap.put(id, new WeakReference(owner)); - idToNameMap.put(id, name); - idToTypeMap.put(id, type); - } - - idToValueMap.put(id, value); - - return id; - } - } - - /** - * Unregisters the variable. - * - * @param name - * the Variable name. - * @param owner - * the Listener for variable changes. - */ - public void unregisterVariable(String name, VariableOwner owner) { - - synchronized (mapLock) { - - // Get the id - HashMap nameToIdMap = (HashMap) ownerToNameToIdMap.get(owner); - if (nameToIdMap == null) - return; - String id = (String) nameToIdMap.get(name); - if (id != null) - return; - - // Remove all the mappings - nameToIdMap.remove(name); - if (nameToIdMap.isEmpty()) - ownerToNameToIdMap.remove(owner); - idToNameMap.remove(id); - idToTypeMap.remove(id); - idToValueMap.remove(id); - idToOwnerMap.remove(id); - - } - } - - /** - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - private class ParameterContainer { - - /** - * Constructs the mapping: listener to set of listened parameter names. - */ - private HashMap parameters = new HashMap(); - - /** - * Parameter values. - */ - private HashMap values = new HashMap(); - - /** - * Multipart parser used for parsing the request. - */ - private ServletMultipartRequest parser = null; - - /** - * Name - Value mapping of parameters that are not variables. - */ - private HashMap nonVariables = new HashMap(); - - /** - * Creates a new parameter container and parse the parameters from the - * request using GET, POST and POST/MULTIPART parsing - * - * @param req - * the Http request to handle. - * @throws IOException - * if the writing failed due to input/output error. - */ - public ParameterContainer(HttpServletRequest req) throws IOException { - // Parse GET / POST parameters - for (Enumeration e = req.getParameterNames(); e.hasMoreElements();) { - String paramName = (String) e.nextElement(); - String[] paramValues = req.getParameterValues(paramName); - addParam(paramName, paramValues); - } - - // Parse multipart variables - try { - parser = new ServletMultipartRequest(req, - MultipartRequest.MAX_READ_BYTES); - } catch (IllegalArgumentException ignored) { - parser = null; - } - - if (parser != null) { - for (Enumeration e = parser.getFileParameterNames(); e - .hasMoreElements();) { - String paramName = (String) e.nextElement(); - addParam(paramName, null); - } - for (Enumeration e = parser.getParameterNames(); e - .hasMoreElements();) { - String paramName = (String) e.nextElement(); - Enumeration val = parser.getURLParameters(paramName); - - // Create a linked list from enumeration to calculate - // elements - LinkedList l = new LinkedList(); - while (val.hasMoreElements()) - l.addLast(val.nextElement()); - - // String array event constructor - String[] s = new String[l.size()]; - Iterator i = l.iterator(); - for (int j = 0; j < s.length; j++) - s[j] = (String) i.next(); - - addParam(paramName, s); - } - } - - } - - /** - * Adds the parameter to container. - * - * @param name - * the Parameter name. - * @param value - * the Parameter value. - */ - private void addParam(String name, String[] value) { - - // Support name="set:name=value" value="ignored" notation - if (name.startsWith("set:")) { - int equalsIndex = name.indexOf('='); - value[0] = name.substring(equalsIndex + 1, name.length()); - name = name.substring(4, equalsIndex); - String[] curVal = (String[]) values.get(name); - if (curVal != null) { - String[] newVal = new String[1 + curVal.length]; - newVal[curVal.length] = value[0]; - for (int i = 0; i < curVal.length; i++) - newVal[i] = curVal[i]; - value = newVal; - - // Special case - if the set:-method is used for - // declaring array of length 2, where either of the - // following conditions are true: - // - the both items are the same - // - the both items have the same length and - // - the items only differ on last character - // - second last character is '.' - // - last char of one string is 'x' and other is 'y' - // Browser is unporposely modifying the name. - if (value.length == 2 - && value[0].length() == value[1].length()) { - boolean same = true; - for (int i = 0; i < value[0].length() - 1 && same; i++) - if (value[0].charAt(i) != value[1].charAt(i)) - same = false; - if (same - && ((value[0].charAt(value[0].length() - 1) == 'x' && value[1] - .charAt(value[1].length() - 1) == 'y') || (value[0] - .charAt(value[0].length() - 1) == 'y' && value[1] - .charAt(value[1].length() - 1) == 'x'))) { - value = new String[] { value[0].substring(0, - value[1].length() - 2) }; - } else if (same && value[0].equals(value[1])) - value = new String[] { value[0] }; - } - - // Special case - if the set:-method is used for - // declaring array of length 3, where all of the - // following conditions are true: - // - two last items have the same length - // - the first item is 2 chars shorter - // - the longer items only differ on last character - // - the shortest item is a prefix of the longer ones - // - second last character of longer ones is '.' - // - last char of one long string is 'x' and other is 'y' - // Browser is unporposely modifying the name. (Mozilla, - // Firefox, ..) - if (value.length == 3 - && value[1].length() == value[2].length() - && value[0].length() + 2 == value[1].length()) { - boolean same = true; - for (int i = 0; i < value[1].length() - 1 && same; i++) - if (value[2].charAt(i) != value[1].charAt(i)) - same = false; - for (int i = 0; i < value[0].length() && same; i++) - if (value[0].charAt(i) != value[1].charAt(i)) - same = false; - if (same - && (value[2].charAt(value[2].length() - 1) == 'x' && value[1] - .charAt(value[1].length() - 1) == 'y') - || (value[2].charAt(value[2].length() - 1) == 'y' && value[1] - .charAt(value[1].length() - 1) == 'x')) { - value = new String[] { value[0] }; - } - } - - } - } - - // Support for setting arrays in format - // set-array:name=value1,value2,value3,... - else if (name.startsWith("set-array:")) { - int equalsIndex = name.indexOf('='); - if (equalsIndex < 0) - return; - - StringTokenizer commalist = new StringTokenizer(name - .substring(equalsIndex + 1), ","); - name = name.substring(10, equalsIndex); - String[] curVal = (String[]) values.get(name); - ArrayList elems = new ArrayList(); - - // Add old values if present. - if (curVal != null) { - for (int i = 0; i < curVal.length; i++) - elems.add(curVal[i]); - } - while (commalist.hasMoreTokens()) { - String token = commalist.nextToken(); - if (token != null && token.length() > 0) - elems.add(token); - } - value = new String[elems.size()]; - for (int i = 0; i < value.length; i++) - value[i] = (String) elems.get(i); - - } - - // Support name="array:name" value="val1,val2,val3" notation - // All the empty elements are ignored - else if (name.startsWith("array:")) { - - name = name.substring(6); - StringTokenizer commalist = new StringTokenizer(value[0], ","); - String[] curVal = (String[]) values.get(name); - ArrayList elems = new ArrayList(); - - // Add old values if present. - if (curVal != null) { - for (int i = 0; i < curVal.length; i++) - elems.add(curVal[i]); - } - while (commalist.hasMoreTokens()) { - String token = commalist.nextToken(); - if (token != null && token.length() > 0) - elems.add(token); - } - value = new String[elems.size()]; - for (int i = 0; i < value.length; i++) - value[i] = (String) elems.get(i); - } - - // Support declaring variables with name="declare:name" - else if (name.startsWith("declare:")) { - name = name.substring(8); - value = (String[]) values.get(name); - if (value == null) - value = new String[0]; - } - - // Gets the owner - WeakReference ref = (WeakReference) idToOwnerMap.get(name); - VariableOwner owner = null; - if (ref != null) - owner = (VariableOwner) ref.get(); - - // Adds the parameter to mapping only if they have owners - if (owner != null) { - Set p = (Set) parameters.get(owner); - if (p == null) - parameters.put(owner, p = new HashSet()); - p.add(name); - if (value != null) - values.put(name, value); - } - - // If the owner can not be found - else { - - // If parameter has been mapped before, remove the old owner - // mapping - if (ref != null) { - - // The owner has been destroyed, so we remove the mappings - idToNameMap.remove(name); - idToOwnerMap.remove(name); - idToTypeMap.remove(name); - idToValueMap.remove(name); - } - - // Adds the parameter to set of non-variables - nonVariables.put(name, value); - } - - } - - /** - * Gets the set of all parameters connected to given variable owner. - * - * @param owner - * the Listener for variable changes. - * @return the set of all the parameters. - */ - public Set getParameters(VariableOwner owner) { - if (owner == null) - return null; - return (Set) parameters.get(owner); - } - - /** - * Gets the set of all variable owners owning parameters in this - * request. - * - * @return the set of all varaible owners. - */ - public Set getOwners() { - return parameters.keySet(); - } - - /** - * Gets the value of a parameter. - * - * @param parameterName - * the name of the parameter. - * @return the value of the parameter. - */ - public String[] getValue(String parameterName) { - return (String[]) values.get(parameterName); - } - - /** - * Gets the servlet multipart parser. - * - * @return the parser. - */ - public ServletMultipartRequest getParser() { - return parser; - } - - /** - * Gets the name - value[] mapping of non variable parameters. - * - * @return the mapping of non variable parameters. - */ - public Map getNonVariables() { - return nonVariables; - } - } - - /** - * Handles all variable changes in this request. - * - * @param req - * the Http request to handle. - * @param errorListener - * the listeners If the list is non null, only the listed - * listeners are served. Otherwise all the listeners are served. - * @return Name to Value[] mapping of unhandled variables. - * @throws IOException - * if the writing failed due to input/output error. - */ - public Map handleVariables(HttpServletRequest req, - Terminal.ErrorListener errorListener) throws IOException { - - // Gets the parameters - ParameterContainer parcon = new ParameterContainer(req); - - // Sorts listeners to dependency order - List listeners = getDependencySortedListenerList(parcon.getOwners()); - - // Handles all parameters for all listeners - while (!listeners.isEmpty()) { - VariableOwner listener = (VariableOwner) listeners.remove(0); - boolean changed = false; // Has any of this owners variabes - // changed - // Handles all parameters for listener - Set params = parcon.getParameters(listener); - if (params != null) { // Name value mapping - Map variables = new HashMap(); - for (Iterator pi = params.iterator(); pi.hasNext();) { - // Gets the name of the parameter - String param = (String) pi.next(); - // Extracts more information about the parameter - String varName = (String) idToNameMap.get(param); - Class varType = (Class) idToTypeMap.get(param); - Object varOldValue = idToValueMap.get(param); - if (varName == null || varType == null) - // TODO Remove this? - System.err - .println("VariableMap: No variable found for parameter " - + param - + " (" - + varName - + "," - + listener + ")"); - else { - - ServletMultipartRequest parser = parcon.getParser(); - - // Uploads events - if (varType.equals(UploadStream.class)) { - if (parser != null - && parser.getFileParameter(param, - MultipartRequest.FILENAME) != null) { - String filename = (String) parser - .getFileParameter(param, - MultipartRequest.FILENAME); - String contentType = (String) parser - .getFileParameter(param, - MultipartRequest.CONTENT_TYPE); - UploadStream upload = new AjaxHttpUploadStream( - varName, parser.getFileContents(param), - filename, contentType); - variables.put(varName, upload); - changed = true; - } - } - - // Normal variable change events - else { - // First try to parse the event without multipart - String[] values = parcon.getValue(param); - if (values != null) { - - if (varType.equals(String[].class)) { - variables.put(varName, values); - changed |= (!Arrays.equals(values, - (String[]) varOldValue)); - } else { - try { - if (values.length == 1) { - Object val = convert(varType, - values[0]); - variables.put(varName, val); - changed |= ((val == null && varOldValue != null) || (val != null && !val - .equals(varOldValue))); - } else if (values.length == 0 - && varType - .equals(Boolean.class)) { - Object val = new Boolean(false); - variables.put(varName, val); - changed |= (!val - .equals(varOldValue)); - } else { - // TODO Remove this? - System.err - .println("Empty variable '" - + varName - + "' of type " - + varType - .toString()); - } - - } catch (java.lang.ClassCastException e) { - // TODO Remove this? - System.err - .println("WebVariableMap conversion exception"); - e.printStackTrace(System.err); - errorListener - .terminalError(new TerminalErrorImpl( - e)); - } - } - } - } - } - } - - // Do the valuechange if the listener is enabled - if (listener.isEnabled() && changed) { - try { - listener.changeVariables(req, variables); - } catch (Throwable t) { - // Notify the error listener - errorListener.terminalError(new VariableOwnerErrorImpl( - listener, t)); - } - } - } - } - - return parcon.getNonVariables(); - } - - /** - * Implementation of VariableOwner.Error interface. - */ - public class TerminalErrorImpl implements Terminal.ErrorEvent { - private Throwable throwable; - - /** - * - * @param throwable - */ - private TerminalErrorImpl(Throwable throwable) { - this.throwable = throwable; - } - - /** - * @see com.itmill.toolkit.terminal.Terminal.ErrorEvent#getThrowable() - */ - public Throwable getThrowable() { - return this.throwable; - } - - } - - /** - * Implementation of VariableOwner.Error interface. - */ - public class VariableOwnerErrorImpl extends TerminalErrorImpl implements - VariableOwner.ErrorEvent { - - private VariableOwner owner; - - /** - * - * @param owner - * the Listener for variable changes. - * @param throwable - */ - private VariableOwnerErrorImpl(VariableOwner owner, Throwable throwable) { - super(throwable); - this.owner = owner; - } - - /** - * @see com.itmill.toolkit.terminal.VariableOwner.ErrorEvent#getVariableOwner() - */ - public VariableOwner getVariableOwner() { - return this.owner; - } - - } - - /** - * Resolves the VariableOwners needed from the request and sort them to - * assure that the dependencies are met (as well as possible). - * - * @param listeners - * @return List of variable list changers, that are needed for handling all - * the variables in the request - */ - private List getDependencySortedListenerList(Set listeners) { - - LinkedList resultNormal = new LinkedList(); - LinkedList resultImmediate = new LinkedList(); - - // Go trough the listeners and either add them to result or resolve - // their dependencies - HashMap deepdeps = new HashMap(); - LinkedList unresolved = new LinkedList(); - for (Iterator li = listeners.iterator(); li.hasNext();) { - - VariableOwner listener = (VariableOwner) li.next(); - if (listener != null) { - Set dependencies = listener.getDirectDependencies(); - - // The listeners with no dependencies are added to the front of - // the - // list directly - if (dependencies == null || dependencies.isEmpty()) { - if (listener.isImmediate()) - resultImmediate.addFirst(listener); - else - resultNormal.addFirst(listener); - } - - // Resolve deep dependencies for the listeners with dependencies - // (the listeners will be added to the end of results in correct - // dependency order later). Also the dependencies of all the - // depended listeners are resolved. - else if (deepdeps.get(listener) == null) { - - // Set the fifo for unresolved parents to contain only the - // listener to be resolved - unresolved.clear(); - unresolved.add(listener); - - // Resolve dependencies - HashSet tmpdeepdeps = new HashSet(); - while (!unresolved.isEmpty()) { - - VariableOwner l = (VariableOwner) unresolved - .removeFirst(); - if (!tmpdeepdeps.contains(l)) { - tmpdeepdeps.add(l); - if (deepdeps.containsKey(l)) { - tmpdeepdeps.addAll((Set) deepdeps.get(l)); - } else { - Set deps = l.getDirectDependencies(); - if (deps != null && !deps.isEmpty()) - for (Iterator di = deps.iterator(); di - .hasNext();) { - Object d = di.next(); - if (d != null - && !tmpdeepdeps.contains(d)) - unresolved.addLast(d); - } - } - } - } - - tmpdeepdeps.remove(listener); - deepdeps.put(listener, tmpdeepdeps); - } - } - } - - // Adds the listeners with dependencies in sane order to the result - for (Iterator li = deepdeps.keySet().iterator(); li.hasNext();) { - VariableOwner l = (VariableOwner) li.next(); - boolean immediate = l.isImmediate(); - - // Adds each listener after the last depended listener already in - // the list - int index = -1; - for (Iterator di = ((Set) deepdeps.get(l)).iterator(); di.hasNext();) { - int k; - Object depended = di.next(); - if (immediate) { - k = resultImmediate.lastIndexOf(depended); - } else { - k = resultNormal.lastIndexOf(depended); - } - if (k > index) - index = k; - } - if (immediate) { - resultImmediate.add(index + 1, l); - } else { - resultNormal.add(index + 1, l); - } - } - - // Appends immediate listeners to normal listeners - // This way the normal handlers are always called before - // immediate ones - resultNormal.addAll(resultImmediate); - return resultNormal; - } -} diff --git a/src/com/itmill/toolkit/terminal/web/AjaxXmlPaintTarget.java b/src/com/itmill/toolkit/terminal/web/AjaxXmlPaintTarget.java deleted file mode 100644 index dc175f731a..0000000000 --- a/src/com/itmill/toolkit/terminal/web/AjaxXmlPaintTarget.java +++ /dev/null @@ -1,608 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import com.itmill.toolkit.Application; -import com.itmill.toolkit.terminal.ApplicationResource; -import com.itmill.toolkit.terminal.ExternalResource; -import com.itmill.toolkit.terminal.PaintException; -import com.itmill.toolkit.terminal.PaintTarget; -import com.itmill.toolkit.terminal.Paintable; -import com.itmill.toolkit.terminal.Resource; -import com.itmill.toolkit.terminal.ThemeResource; -import com.itmill.toolkit.terminal.UploadStream; -import com.itmill.toolkit.terminal.VariableOwner; - -import java.io.BufferedWriter; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.io.UnsupportedEncodingException; -import java.util.HashSet; -import java.util.Set; -import java.util.Stack; - -/** - * User Interface Description Language Target. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.1 - */ -public class AjaxXmlPaintTarget implements PaintTarget, AjaxPaintTarget { - - /* Document type declarations */ - private final static String UIDL_XML_DECL = ""; - - private final static String UIDL_ARG_NAME = "name"; - - private final static String UIDL_ARG_VALUE = "value"; - - private final static String UIDL_ARG_ID = "id"; - - private Stack mOpenTags; - - private boolean mTagArgumentListOpen; - - private PrintWriter uidlBuffer; - - private AjaxVariableMap variableMap; - - private boolean closed = false; - - private AjaxApplicationManager manager; - - private boolean trackPaints = false; - - private int numberOfPaints = 0; - - private Set preCachedResources = new HashSet(); - private boolean customLayoutArgumentsOpen = false; - - /** - * Creates a new XMLPrintWriter, without automatic line flushing. - * - * @param variableMap - * @param manager - * @param output - * A character-output stream. - * @throws PaintException - * if the paint operation failed. - */ - public AjaxXmlPaintTarget(AjaxVariableMap variableMap, - AjaxApplicationManager manager, OutputStream output) - throws PaintException { - - // Sets the cache - this.manager = manager; - - // Sets the variable map - this.variableMap = variableMap; - - // Sets the target for UIDL writing - try { - this.uidlBuffer = new PrintWriter(new BufferedWriter( - new OutputStreamWriter(output, "UTF-8"))); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException("Internal error"); - } - - // Initialize tag-writing - mOpenTags = new Stack(); - mTagArgumentListOpen = false; - - // Adds document declaration - this.print(UIDL_XML_DECL + "\n\n"); - - // Adds UIDL start tag and its attributes - this.startTag("changes"); - - } - - /** - * Ensures that the currently open element tag is closed. - */ - private void ensureClosedTag() { - if (mTagArgumentListOpen) { - append(">"); - mTagArgumentListOpen = false; - customLayoutArgumentsOpen = false; - } - } - - /** - * Method append.This method is thread safe. - * - * @param string - * the text to insert. - */ - private void append(String string) { - uidlBuffer.print(string); - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#startTag(java.lang.String) - */ - public void startTag(String tagName) throws PaintException { - // In case of null data output nothing: - if (tagName == null) - throw new NullPointerException(); - - // Increments paint tracker - if (this.isTrackPaints()) { - this.numberOfPaints++; - } - - // Ensures that the target is open - if (this.closed) - throw new PaintException( - "Attempted to write to a closed PaintTarget."); - - // Makes sure that the open start tag is closed before - // anything is written. - ensureClosedTag(); - - // Checks tagName and attributes here - mOpenTags.push(tagName); - - // Prints the tag with attributes - append("<" + tagName); - - mTagArgumentListOpen = true; - - if ("customlayout".equals(tagName)) - customLayoutArgumentsOpen = true; - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#endTag(java.lang.String) - */ - public void endTag(String tagName) throws PaintException { - // In case of null data output nothing: - if (tagName == null) - throw new NullPointerException(); - - // Ensure that the target is open - if (this.closed) - throw new PaintException( - "Attempted to write to a closed PaintTarget."); - - String lastTag = ""; - - lastTag = (String) mOpenTags.pop(); - if (!tagName.equalsIgnoreCase(lastTag)) - throw new PaintException("Invalid UIDL: wrong ending tag: '" - + tagName + "' expected: '" + lastTag + "'."); - - // Make sure that the open start tag is closed before - // anything is written. - if (mTagArgumentListOpen) { - append(">"); - mTagArgumentListOpen = false; - customLayoutArgumentsOpen = false; - } - - // Writes the end (closing) tag - append(""); - flush(); - } - - /** - * Substitutes the XML sensitive characters with predefined XML entities. - * - * @param xml - * the String to be substituted. - * @return A new string instance where all occurrences of XML sensitive - * characters are substituted with entities. - */ - static public String escapeXML(String xml) { - if (xml == null || xml.length() <= 0) - return ""; - return escapeXML(new StringBuffer(xml)).toString(); - } - - /** - * Substitutes the XML sensitive characters with predefined XML entities. - * - * @param xml - * the String to be substituted. - * @return A new StringBuffer instance where all occurrences of XML - * sensitive characters are substituted with entities. - * - */ - static public StringBuffer escapeXML(StringBuffer xml) { - if (xml == null || xml.length() <= 0) - return new StringBuffer(""); - - StringBuffer result = new StringBuffer(xml.length() * 2); - - for (int i = 0; i < xml.length(); i++) { - char c = xml.charAt(i); - String s = toXmlChar(c); - if (s != null) { - result.append(s); - } else { - result.append(c); - } - } - return result; - } - - /** - * Substitutes a XML sensitive character with predefined XML entity. - * - * @param c - * the Character to be replaced with an entity. - * @return String of the entity or null if character is not to be replaced - * with an entity. - */ - private static String toXmlChar(char c) { - switch (c) { - case '&': - return "&"; // & => & - case '>': - return ">"; // > => > - case '<': - return "<"; // < => < - case '"': - return """; // " => " - case '\'': - return "'"; // ' => ' - default: - return null; - } - } - - /** - * Prints XML. - * - * Writes pre-formatted XML to stream. Well-formness of XML is checked. - * - *
-	 * 
-	 *  TODO: XML checking should be made
-	 *  
-	 * 
- * - * @param str - * the string to print. - */ - private void print(String str) { - // In case of null data output nothing: - if (str == null) - return; - - // Make sure that the open start tag is closed before - // anything is written. - ensureClosedTag(); - - // Write what was given - append(str); - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#addText(java.lang.String) - */ - public void addText(String str) throws PaintException { - addUIDL(escapeXML(str)); - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#addAttribute(java.lang.String, boolean) - */ - public void addAttribute(String name, boolean value) throws PaintException { - addAttribute(name, String.valueOf(value)); - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#addAttribute(java.lang.String, com.itmill.toolkit.terminal.Resource) - */ - public void addAttribute(String name, Resource value) throws PaintException { - - if (value instanceof ExternalResource) { - addAttribute(name, ((ExternalResource) value).getURL()); - - } else if (value instanceof ApplicationResource) { - ApplicationResource r = (ApplicationResource) value; - Application a = r.getApplication(); - if (a == null) - throw new PaintException( - "Application not specified for resorce " - + value.getClass().getName()); - String uri = a.getURL().getPath(); - if (uri.charAt(uri.length() - 1) != '/') - uri += "/"; - uri += a.getRelativeLocation(r); - addAttribute(name, uri); - - } else if (value instanceof ThemeResource) { - String uri = "theme://" + ((ThemeResource) value).getResourceId(); - addAttribute(name, uri); - } else - throw new PaintException("Ajax adapter does not " - + "support resources of type: " - + value.getClass().getName()); - - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#addAttribute(java.lang.String, int) - */ - public void addAttribute(String name, int value) throws PaintException { - addAttribute(name, String.valueOf(value)); - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#addAttribute(java.lang.String, long) - */ - public void addAttribute(String name, long value) throws PaintException { - addAttribute(name, String.valueOf(value)); - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#addAttribute(java.lang.String, java.lang.String) - */ - public void addAttribute(String name, String value) throws PaintException { - // In case of null data output nothing: - if ((value == null) || (name == null)) - throw new NullPointerException( - "Parameters must be non-null strings"); - - // Ensure that the target is open - if (this.closed) - throw new PaintException( - "Attempted to write to a closed PaintTarget."); - - // Check that argument list is writable. - if (!mTagArgumentListOpen) - throw new PaintException("XML argument list not open."); - - append(" " + name + "=\"" + escapeXML(value) + "\""); - - if (customLayoutArgumentsOpen && "style".equals(name)) - getPreCachedResources().add("layout/" + value + ".html"); - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#addVariable(com.itmill.toolkit.terminal.VariableOwner, java.lang.String, java.lang.String) - */ - public void addVariable(VariableOwner owner, String name, String value) - throws PaintException { - String code = variableMap.registerVariable(name, String.class, value, - owner); - startTag("string"); - addAttribute(UIDL_ARG_ID, code); - addAttribute(UIDL_ARG_NAME, name); - addText(value); - endTag("string"); - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#addVariable(com.itmill.toolkit.terminal.VariableOwner, java.lang.String, int) - */ - public void addVariable(VariableOwner owner, String name, int value) - throws PaintException { - String code = variableMap.registerVariable(name, Integer.class, - new Integer(value), owner); - startTag("integer"); - addAttribute(UIDL_ARG_ID, code); - addAttribute(UIDL_ARG_NAME, name); - addAttribute(UIDL_ARG_VALUE, String.valueOf(value)); - endTag("integer"); - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#addVariable(com.itmill.toolkit.terminal.VariableOwner, java.lang.String, boolean) - */ - public void addVariable(VariableOwner owner, String name, boolean value) - throws PaintException { - String code = variableMap.registerVariable(name, Boolean.class, - new Boolean(value), owner); - startTag("boolean"); - addAttribute(UIDL_ARG_ID, code); - addAttribute(UIDL_ARG_NAME, name); - addAttribute(UIDL_ARG_VALUE, String.valueOf(value)); - endTag("boolean"); - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#addVariable(com.itmill.toolkit.terminal.VariableOwner, java.lang.String, java.lang.String[]) - */ - public void addVariable(VariableOwner owner, String name, String[] value) - throws PaintException { - String code = variableMap.registerVariable(name, String[].class, value, - owner); - startTag("array"); - addAttribute(UIDL_ARG_ID, code); - addAttribute(UIDL_ARG_NAME, name); - for (int i = 0; i < value.length; i++) - addSection("ai", value[i]); - endTag("array"); - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#addUploadStreamVariable(com.itmill.toolkit.terminal.VariableOwner, java.lang.String) - */ - public void addUploadStreamVariable(VariableOwner owner, String name) - throws PaintException { - String code = variableMap.registerVariable(name, UploadStream.class, - null, owner); - startTag("uploadstream"); - addAttribute(UIDL_ARG_ID, code); - addAttribute(UIDL_ARG_NAME, name); - endTag("uploadstream"); - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#addSection(java.lang.String, java.lang.String) - */ - public void addSection(String sectionTagName, String sectionData) - throws PaintException { - startTag(sectionTagName); - addText(sectionData); - endTag(sectionTagName); - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#addUIDL(java.lang.String) - */ - public void addUIDL(String xml) throws PaintException { - - // Ensure that the target is open - if (this.closed) - throw new PaintException( - "Attempted to write to a closed PaintTarget."); - - // Make sure that the open start tag is closed before - // anything is written. - ensureClosedTag(); - - // Escape and write what was given - if (xml != null) - append(xml); - - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#addXMLSection(java.lang.String, java.lang.String, java.lang.String) - */ - public void addXMLSection(String sectionTagName, String sectionData, - String namespace) throws PaintException { - - // Ensure that the target is open - if (this.closed) - throw new PaintException( - "Attempted to write to a closed PaintTarget."); - - startTag(sectionTagName); - if (namespace != null) - addAttribute("xmlns", namespace); - append(">"); - mTagArgumentListOpen = false; - - if (sectionData != null) - append(sectionData); - endTag(sectionTagName); - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#getUIDL() - */ - public String getUIDL() { - if (this.closed) { - return uidlBuffer.toString(); - } - throw new IllegalStateException( - "Tried to read UIDL from open PaintTarget"); - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#close() - */ - public void close() throws PaintException { - if (!this.closed) { - this.endTag("changes"); - flush(); - - // Close all - this.uidlBuffer.close(); - this.closed = true; - } - } - - /** - * Method flush. - */ - private void flush() { - this.uidlBuffer.flush(); - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#startTag(com.itmill.toolkit.terminal.Paintable, java.lang.String) - */ - public boolean startTag(Paintable paintable, String tag) - throws PaintException { - startTag(tag); - String id = manager.getPaintableId(paintable); - paintable.addListener(manager); - addAttribute("id", id); - return false; - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#addCharacterData(java.lang.String) - */ - public void addCharacterData(String text) throws PaintException { - ensureClosedTag(); - if (text != null) - append(""); - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#isTrackPaints() - */ - public boolean isTrackPaints() { - return trackPaints; - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#getNumberOfPaints() - */ - public int getNumberOfPaints() { - return numberOfPaints; - } - - /* (non-Javadoc) - * @see com.itmill.toolkit.terminal.web.AjaxPaintTarget#setTrackPaints(boolean) - */ - public void setTrackPaints(boolean enabled) { - this.trackPaints = enabled; - this.numberOfPaints = 0; - } - - public void setPreCachedResources(Set preCachedResources) { - this.preCachedResources = preCachedResources; - } - - public Set getPreCachedResources() { - return preCachedResources; - } - - public void addAttribute(String string, String[] keys) { - // TODO Auto-generated method stub - - } - - public void addAttribute(String string, Object[] keys) { - // TODO Auto-generated method stub - - } - -} diff --git a/src/com/itmill/toolkit/terminal/web/ApplicationServlet.java b/src/com/itmill/toolkit/terminal/web/ApplicationServlet.java deleted file mode 100644 index 525d9f91b6..0000000000 --- a/src/com/itmill/toolkit/terminal/web/ApplicationServlet.java +++ /dev/null @@ -1,2186 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileOutputStream; -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.net.MalformedURLException; -import java.net.URL; -import java.util.Arrays; -import java.util.Collection; -import java.util.Date; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.StringTokenizer; -import java.util.Vector; -import java.util.WeakHashMap; - -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; -import javax.servlet.http.HttpSessionBindingEvent; -import javax.servlet.http.HttpSessionBindingListener; - -import org.xml.sax.SAXException; - -import com.itmill.toolkit.Application; -import com.itmill.toolkit.Application.WindowAttachEvent; -import com.itmill.toolkit.Application.WindowDetachEvent; -import com.itmill.toolkit.service.FileTypeResolver; -import com.itmill.toolkit.service.License; -import com.itmill.toolkit.service.License.InvalidLicenseFile; -import com.itmill.toolkit.service.License.LicenseFileHasNotBeenRead; -import com.itmill.toolkit.service.License.LicenseSignatureIsInvalid; -import com.itmill.toolkit.service.License.LicenseViolation; -import com.itmill.toolkit.terminal.DownloadStream; -import com.itmill.toolkit.terminal.Paintable; -import com.itmill.toolkit.terminal.ParameterHandler; -import com.itmill.toolkit.terminal.ThemeResource; -import com.itmill.toolkit.terminal.URIHandler; -import com.itmill.toolkit.terminal.Paintable.RepaintRequestEvent; -import com.itmill.toolkit.terminal.web.ThemeSource.ThemeException; -import com.itmill.toolkit.terminal.web.WebBrowser; -import com.itmill.toolkit.ui.Window; - -/** - * This servlet connects IT Mill Toolkit Application to Web. This servlet - * replaces both WebAdapterServlet and AjaxAdapterServlet. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 4.0 - */ - -public class ApplicationServlet extends HttpServlet implements - Application.WindowAttachListener, Application.WindowDetachListener, - Paintable.RepaintRequestListener { - - private static final long serialVersionUID = -4937882979845826574L; - - /** - * Version number of this release. For example "4.0.0". - */ - public static final String VERSION; - - /** - * Major version number. For example 4 in 4.1.0. - */ - public static final int VERSION_MAJOR; - - /** - * Minor version number. For example 1 in 4.1.0. - */ - public static final int VERSION_MINOR; - - /** - * Builds number. For example 0-beta1 in 4.0.0-beta1. - */ - public static final String VERSION_BUILD; - - /* Initialize version numbers from string replaced by build-script. */ - static { - if ("@VERSION@".equals("@" + "VERSION" + "@")) - VERSION = "4.0.0-INTERNAL-NONVERSIONED-DEBUG-BUILD"; - else - VERSION = "@VERSION@"; - String[] digits = VERSION.split("\\."); - VERSION_MAJOR = Integer.parseInt(digits[0]); - VERSION_MINOR = Integer.parseInt(digits[1]); - VERSION_BUILD = digits[2]; - } - - // Configurable parameter names - private static final String PARAMETER_DEBUG = "Debug"; - - private static final String PARAMETER_DEFAULT_THEME_JAR = "DefaultThemeJar"; - - private static final String PARAMETER_THEMESOURCE = "ThemeSource"; - - private static final String PARAMETER_THEME_CACHETIME = "ThemeCacheTime"; - - private static final String PARAMETER_MAX_TRANSFORMERS = "MaxTransformers"; - - private static final String PARAMETER_TRANSFORMER_CACHETIME = "TransformerCacheTime"; - - private static final int DEFAULT_THEME_CACHETIME = 1000 * 60 * 60 * 24; - - private static final int DEFAULT_BUFFER_SIZE = 32 * 1024; - - private static final int DEFAULT_MAX_TRANSFORMERS = 1; - - private static final int MAX_BUFFER_SIZE = 64 * 1024; - - // TODO: these should be moved to session object and stored directly into - // session - private static final String SESSION_ATTR_VARMAP = "itmill-toolkit-varmap"; - - private static final String SESSION_ATTR_CONTEXT = "itmill-toolkit-context"; - - protected static final String SESSION_ATTR_APPS = "itmill-toolkit-apps"; - - private static final String SESSION_BINDING_LISTENER = "itmill-toolkit-bindinglistener"; - - private static HashMap applicationToDirtyWindowSetMap = new HashMap(); - - private static HashMap applicationToServerCommandStreamLock = new HashMap(); - - private static HashMap applicationToLastRequestDate = new HashMap(); - - private static HashMap applicationToAjaxAppMgrMap = new HashMap(); - - // License for ApplicationServlets - private static HashMap licenseForApplicationClass = new HashMap(); - - private static HashMap licensePrintedForApplicationClass = new HashMap(); - - // TODO Should default or base theme be the default? - protected static final String DEFAULT_THEME = "base"; - - private static final String RESOURCE_URI = "/RES/"; - - private static final String AJAX_UIDL_URI = "/UIDL/"; - - private static final String THEME_DIRECTORY_PATH = "/theme/"; - - private static final String THEME_LISTING_FILE = THEME_DIRECTORY_PATH - + "themes.txt"; - - private static final String DEFAULT_THEME_JAR_PREFIX = "itmill-toolkit-themes"; - - private static final String DEFAULT_THEME_JAR = "/WEB-INF/lib/" - + DEFAULT_THEME_JAR_PREFIX + "-" + VERSION + ".jar"; - - private static final String DEFAULT_THEME_TEMP_FILE_PREFIX = "ITMILL_TMP_"; - - private static final String SERVER_COMMAND_PARAM = "SERVER_COMMANDS"; - - private static final int SERVER_COMMAND_STREAM_MAINTAIN_PERIOD = 15000; - - private static final int SERVER_COMMAND_HEADER_PADDING = 2000; - - // Maximum delay between request for an user to be considered active (in ms) - private static final long ACTIVE_USER_REQUEST_INTERVAL = 1000 * 45; - - // Private fields - private Class applicationClass; - - private Properties applicationProperties; - - private UIDLTransformerFactory transformerFactory; - - private CollectionThemeSource themeSource; - - private String resourcePath = null; - - private String debugMode = ""; - - private int maxConcurrentTransformers; - - private long transformerCacheTime; - - private long themeCacheTime; - - /** - * Called by the servlet container to indicate to a servlet that the servlet - * is being placed into service. - * - * @param servletConfig - * the object containing the servlet's configuration and - * initialization parameters - * @throws javax.servlet.ServletException - * if an exception has occurred that interferes with the - * servlet's normal operation. - */ - public void init(javax.servlet.ServletConfig servletConfig) - throws javax.servlet.ServletException { - super.init(servletConfig); - - // Gets the application class name - String applicationClassName = servletConfig - .getInitParameter("application"); - if (applicationClassName == null) { - Log.error("Application not specified in servlet parameters"); - } - - // Stores the application parameters into Properties object - this.applicationProperties = new Properties(); - for (Enumeration e = servletConfig.getInitParameterNames(); e - .hasMoreElements();) { - String name = (String) e.nextElement(); - this.applicationProperties.setProperty(name, servletConfig - .getInitParameter(name)); - } - - // Overrides with server.xml parameters - ServletContext context = servletConfig.getServletContext(); - for (Enumeration e = context.getInitParameterNames(); e - .hasMoreElements();) { - String name = (String) e.nextElement(); - this.applicationProperties.setProperty(name, context - .getInitParameter(name)); - } - - // Gets the debug window parameter - String debug = getApplicationOrSystemProperty(PARAMETER_DEBUG, "") - .toLowerCase(); - - // Enables application specific debug - if (!"".equals(debug) && !"true".equals(debug) - && !"false".equals(debug)) - throw new ServletException( - "If debug parameter is given for an application, it must be 'true' or 'false'"); - this.debugMode = debug; - - // Gets the maximum number of simultaneous transformers - this.maxConcurrentTransformers = Integer - .parseInt(getApplicationOrSystemProperty( - PARAMETER_MAX_TRANSFORMERS, "-1")); - if (this.maxConcurrentTransformers < 1) - this.maxConcurrentTransformers = DEFAULT_MAX_TRANSFORMERS; - - // Gets cache time for transformers - this.transformerCacheTime = Integer - .parseInt(getApplicationOrSystemProperty( - - PARAMETER_TRANSFORMER_CACHETIME, "-1")) * 1000; - - // Gets cache time for theme resources - this.themeCacheTime = Integer.parseInt(getApplicationOrSystemProperty( - PARAMETER_THEME_CACHETIME, "-1")) * 1000; - if (this.themeCacheTime < 0) { - this.themeCacheTime = DEFAULT_THEME_CACHETIME; - } - - // Adds all specified theme sources - this.themeSource = new CollectionThemeSource(); - List directorySources = getThemeSources(); - for (Iterator i = directorySources.iterator(); i.hasNext();) { - this.themeSource.add((ThemeSource) i.next()); - } - - // Adds the default theme source - String[] defaultThemeFiles = new String[] { getApplicationOrSystemProperty( - PARAMETER_DEFAULT_THEME_JAR, DEFAULT_THEME_JAR) }; - File f = findDefaultThemeJar(defaultThemeFiles); - boolean defaultThemeFound = false; - try { - // Adds themes.jar if exists - if (f != null && f.exists()) { - this.themeSource.add(new JarThemeSource(f, this, "")); - defaultThemeFound = true; - } - } catch (Exception e) { - throw new ServletException("Failed to load default theme from " - + Arrays.asList(defaultThemeFiles), e); - } - - // Checks that at least one themesource was loaded - if (this.themeSource.getThemes().size() <= 0) { - throw new ServletException( - "No themes found in specified themesources. " - + Theme.MESSAGE_CONFIGURE_HELP); - } - - // Warn if default theme not found - if (this.themeSource.getThemeByName(DEFAULT_THEME) == null) { - if (!defaultThemeFound) - Log.warn("Default theme JAR not found in: " - + Arrays.asList(defaultThemeFiles)); - } - - // Initializes the transformer factory, if not initialized - if (this.transformerFactory == null) { - this.transformerFactory = new UIDLTransformerFactory( - this.themeSource, this, this.maxConcurrentTransformers, - this.transformerCacheTime); - } - - // Loads the application class using the same class loader - // as the servlet itself - ClassLoader loader = this.getClass().getClassLoader(); - try { - this.applicationClass = loader.loadClass(applicationClassName); - } catch (ClassNotFoundException e) { - throw new ServletException("Failed to load application class: " - + applicationClassName); - } - } - - /** - * Gets an application or system property value. - * - * @param parameterName - * the Name or the parameter. - * @param defaultValue - * the Default to be used. - * @return String value or default if not found - */ - private String getApplicationOrSystemProperty(String parameterName, - String defaultValue) { - - // Try application properties - String val = this.applicationProperties.getProperty(parameterName); - if (val != null) { - return val; - } - - // Try lowercased application properties for backward compability with - // 3.0.2 and earlier - val = this.applicationProperties.getProperty(parameterName - .toLowerCase()); - if (val != null) { - return val; - } - - // Try system properties - String pkgName; - Package pkg = this.getClass().getPackage(); - if (pkg != null) { - pkgName = pkg.getName(); - } else { - String className = this.getClass().getName(); - pkgName = new String(className.toCharArray(), 0, className - .lastIndexOf('.')); - } - val = System.getProperty(pkgName + "." + parameterName); - if (val != null) { - return val; - } - - // Try lowercased system properties - val = System.getProperty(pkgName + "." + parameterName.toLowerCase()); - if (val != null) { - return val; - } - - return defaultValue; - } - - /** - * Gets ThemeSources from given path. Construct the list of avalable themes - * in path using the following sources: - *

- * 1. Content of THEME_PATH directory (if available). - *

- *

- * 2. The themes listed in THEME_LIST_FILE. - *

- *

- * 3. "themesource" application parameter - "ThemeSource" system property. - *

- * - * @return the List - * @throws ServletException - * if an exception has occurred that interferes with the - * servlet's normal operation. - */ - private List getThemeSources() throws ServletException { - - List returnValue = new LinkedList(); - - // Check the list file in theme directory - List sourcePaths = new LinkedList(); - try { - BufferedReader reader = new BufferedReader(new InputStreamReader( - this.getServletContext().getResourceAsStream( - THEME_LISTING_FILE))); - String line = null; - while ((line = reader.readLine()) != null) { - sourcePaths.add(THEME_DIRECTORY_PATH + line.trim()); - } - if (this.isDebugMode(null)) { - Log.debug("Listed " + sourcePaths.size() + " themes in " - + THEME_LISTING_FILE + ". Loading " + sourcePaths); - } - } catch (Exception ignored) { - // If the file reading fails, just skip to next method - } - - // If no file was found or it was empty, - // try to add themes filesystem directory if it is accessible - if (sourcePaths.size() <= 0) { - if (this.isDebugMode(null)) { - Log.debug("No themes listed in " + THEME_LISTING_FILE - + ". Trying to read the content of directory " - + THEME_DIRECTORY_PATH); - } - - try { - String path = getResourcePath(getServletContext(), - THEME_DIRECTORY_PATH); - if (path != null) { - File f = new File(path); - if (f != null && f.exists()) - returnValue.add(new DirectoryThemeSource(f, this)); - } - } catch (java.io.IOException je) { - Log.info("Theme directory " + THEME_DIRECTORY_PATH - + " not available. Skipped."); - } catch (ThemeException e) { - throw new ServletException("Failed to load themes from " - + THEME_DIRECTORY_PATH, e); - } - } - - // Adds the theme sources from application properties - String paramValue = getApplicationOrSystemProperty( - PARAMETER_THEMESOURCE, null); - if (paramValue != null) { - StringTokenizer st = new StringTokenizer(paramValue, ";"); - while (st.hasMoreTokens()) { - sourcePaths.add(st.nextToken()); - } - } - - // Constructs appropriate theme source instances for each path - for (Iterator i = sourcePaths.iterator(); i.hasNext();) { - String source = (String) i.next(); - File sourceFile = new File(source); - try { - // Relative files are treated as streams (to support - // resource inside WAR files) - if (!sourceFile.isAbsolute()) { - returnValue.add(new ServletThemeSource(this - .getServletContext(), this, source)); - } else if (sourceFile.isDirectory()) { - // Absolute directories are read from filesystem - returnValue.add(new DirectoryThemeSource(sourceFile, this)); - } else { - // Absolute JAR-files are read from filesystem - returnValue.add(new JarThemeSource(sourceFile, this, "")); - } - } catch (Exception e) { - // Any exception breaks the the init - throw new ServletException("Invalid theme source: " + source, e); - } - } - - // Returns the constructed list of theme sources - return returnValue; - } - - /** - * Receives standard HTTP requests from the public service method and - * dispatches them. - * - * @param request - * the object that contains the request the client made of the - * servlet. - * @param response - * the object that contains the response the servlet returns to - * the client. - * @throws ServletException - * if an input or output error occurs while the servlet is - * handling the TRACE request. - * @throws IOException - * if the request for the TRACE cannot be handled. - */ - protected void service(HttpServletRequest request, - HttpServletResponse response) throws ServletException, IOException { - - // Transformer and output stream for the result - UIDLTransformer transformer = null; - HttpVariableMap variableMap = null; - OutputStream out = response.getOutputStream(); - HashMap currentlyDirtyWindowsForThisApplication = new HashMap(); - Application application = null; - try { - - // Handles resource requests - if (handleResourceRequest(request, response)) - return; - - // Handles server commands - if (handleServerCommands(request, response)) - return; - - // Gets the application - application = getApplication(request); - - // Creates application if it doesn't exist - if (application == null) - application = createApplication(request); - - // Sets the last application request date - synchronized (applicationToLastRequestDate) { - applicationToLastRequestDate.put(application, new Date()); - } - - // Invokes context transaction listeners - ((WebApplicationContext) application.getContext()) - .startTransaction(application, request); - - // 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) { - - // Handles AJAX UIDL requests - String resourceId = request.getPathInfo(); - if (resourceId != null && resourceId.startsWith(AJAX_UIDL_URI)) { - getApplicationManager(application).handleUidlRequest( - request, response, themeSource, true); - return; - } - - // Gets the variable map - variableMap = getVariableMap(application, request); - if (variableMap == null) - return; - - // Change all variables based on request parameters - Map unhandledParameters = variableMap.handleVariables(request, - application); - - // Check/handle client side feature checks - WebBrowserProbe - .handleProbeRequest(request, unhandledParameters); - - // If rendering mode is not defined or detecting requested - // try to detect it - WebBrowser wb = WebBrowserProbe.getTerminalType(request - .getSession()); - - boolean detect = false; - if (unhandledParameters.get("renderingMode") != null) { - detect = ((String) ((Object[]) unhandledParameters - .get("renderingMode"))[0]).equals("detect"); - } - if (detect - || wb.getRenderingMode() == WebBrowser.RENDERING_MODE_UNDEFINED) { - String themeName = application.getTheme(); - if (themeName == null) - themeName = DEFAULT_THEME; - if (unhandledParameters.get("theme") != null) { - themeName = (String) ((Object[]) unhandledParameters - .get("theme"))[0]; - } - - Theme theme = themeSource.getThemeByName(themeName); - if (theme == null) - throw new ServletException( - "Failed to load theme with name " + themeName - + ". " + Theme.MESSAGE_CONFIGURE_HELP); - - String renderingMode = theme.getPreferredMode(wb, - themeSource); - if (Theme.MODE_AJAX.equals(renderingMode)) { - wb.setRenderingMode(WebBrowser.RENDERING_MODE_AJAX); - } else { - wb.setRenderingMode(WebBrowser.RENDERING_MODE_HTML); - } - } - if (unhandledParameters.get("renderingMode") != null) { - String renderingMode = (String) ((Object[]) unhandledParameters - .get("renderingMode"))[0]; - if (renderingMode.equals("html")) { - wb.setRenderingMode(WebBrowser.RENDERING_MODE_HTML); - } else if (renderingMode.equals("ajax")) { - wb.setRenderingMode(WebBrowser.RENDERING_MODE_AJAX); - } - } - - // 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) { - - // Window renders are not cacheable - response.setHeader("Cache-Control", "no-cache"); - response.setHeader("Pragma", "no-cache"); - response.setDateHeader("Expires", 0); - - // Finds the window within the application - Window window = null; - if (application.isRunning()) - window = getApplicationWindow(request, application, - unhandledParameters); - - // Handles the unhandled parameters if the application is - // still running - if (window != null && unhandledParameters != null - && !unhandledParameters.isEmpty()) { - try { - window.handleParameters(unhandledParameters); - } catch (Throwable t) { - application - .terminalError(new ParameterHandlerErrorImpl( - window, t)); - } - } - - // Removes application if it has stopped - if (!application.isRunning()) { - endApplication(request, response, application); - return; - } - - // Returns blank page, if no window found - if (window == null) { - response.setContentType("text/html"); - BufferedWriter page = new BufferedWriter( - new OutputStreamWriter(out)); - page.write(""); - page - .write("The requested window has been removed from application."); - page.write(""); - page.close(); - - return; - } - - // Sets terminal type for the window, if not already set - if (window.getTerminal() == null) { - window.setTerminal(wb); - } - - // Finds theme - String themeName = window.getTheme() != null ? window - .getTheme() : DEFAULT_THEME; - if (unhandledParameters.get("theme") != null) { - themeName = (String) ((Object[]) unhandledParameters - .get("theme"))[0]; - } - Theme theme = themeSource.getThemeByName(themeName); - if (theme == null) - throw new ServletException("Theme (named '" + themeName - + "') can not be found"); - - // If in ajax rendering mode, print an html page for it - if (wb.getRenderingMode() == WebBrowser.RENDERING_MODE_AJAX) { - writeAjaxPage(request, response, out, - unhandledParameters, window, wb, theme); - return; - } - - // If other than html or ajax mode is requested - if (wb.getRenderingMode() == WebBrowser.RENDERING_MODE_UNDEFINED - && !(window instanceof DebugWindow)) { - // TODO More informal message should be given to browser - response.setContentType("text/html"); - BufferedWriter page = new BufferedWriter( - new OutputStreamWriter(out)); - page.write(""); - page.write("Unsupported browser."); - page.write(""); - page.close(); - - return; - } - - // Initialize Transformer - UIDLTransformerType transformerType = new UIDLTransformerType( - wb, theme); - - transformer = this.transformerFactory - .getTransformer(transformerType); - - // Sets the response type - response.setContentType(wb.getContentType()); - - // Creates UIDL writer - WebPaintTarget paintTarget = transformer - .getPaintTarget(variableMap); - - // Assures that the correspoding debug window will be - // repainted property - // by clearing it before the actual paint. - DebugWindow debugWindow = (DebugWindow) application - .getWindow(DebugWindow.WINDOW_NAME); - if (debugWindow != null && debugWindow != window) { - debugWindow.setWindowUIDL(window, "Painting..."); - } - - // Paints window - window.paint(paintTarget); - paintTarget.close(); - - // For exception handling, memorize the current dirty status - HashMap dirtyWindows = (HashMap) applicationToDirtyWindowSetMap - .get(application); - - if (dirtyWindows == null) { - dirtyWindows = new HashMap(); - applicationToDirtyWindowSetMap.put(application, - dirtyWindows); - } - currentlyDirtyWindowsForThisApplication - .putAll((Map) dirtyWindows); - - // Window is now painted - windowPainted(application, window); - - // Debug - if (debugWindow != null && debugWindow != window) { - debugWindow - .setWindowUIDL(window, paintTarget.getUIDL()); - } - - // Sets the function library state for this thread - ThemeFunctionLibrary.setState(application, window, - transformerType.getWebBrowser(), request - .getSession(), this, transformerType - .getTheme().getName()); - - } - } - - // For normal requests, transform the window - if (download == null) { - - // Transform and output the result to browser - // Note that the transform and transfer of the result is - // not synchronized with the variable map. This allows - // parallel transfers and transforms for better performance, - // but requires that all calls from the XSL to java are - // thread-safe - transformer.transform(out); - } - - // For download request, transfer the downloaded data - else { - - handleDownload(download, request, response); - } - - } catch (UIDLTransformerException te) { - // Print stacktrace - te.printStackTrace(); - - try { - // Writes the error report to client - response.setContentType("text/html"); - BufferedWriter err = new BufferedWriter(new OutputStreamWriter( - out)); - err - .write("Application Internal Error"); - err.write("

" + te.getMessage() + "

"); - err.write(te.getHTMLDescription()); - err.write(""); - err.close(); - } catch (Throwable t) { - Log.except("Failed to write error page: " + t - + ". Original exception was: ", te); - } - - // Adds previously dirty windows to dirtyWindowList in order - // to make sure that eventually they are repainted - Application currentApplication = getApplication(request); - for (Iterator iter = currentlyDirtyWindowsForThisApplication - .keySet().iterator(); iter.hasNext();) { - Window dirtyWindow = (Window) iter.next(); - addDirtyWindow(currentApplication, dirtyWindow); - } - - } catch (Throwable e) { - // Print stacktrace - e.printStackTrace(); - // Re-throw other exceptions - throw new ServletException(e); - } finally { - - // Releases transformer - if (transformer != null) - transformerFactory.releaseTransformer(transformer); - - // Notifies transaction end - if (application != null) - ((WebApplicationContext) application.getContext()) - .endTransaction(application, request); - - // Cleans the function library state for this thread - // for security reasons - ThemeFunctionLibrary.cleanState(); - } - } - - /** - * - * @param request - * the HTTP request. - * @param response - * the HTTP response to write to. - * @param out - * @param unhandledParameters - * @param window - * @param terminalType - * @param theme - * @throws IOException - * if the writing failed due to input/output error. - * @throws MalformedURLException - * if the application is denied access the persistent data store - * represented by the given URL. - */ - private void writeAjaxPage(HttpServletRequest request, - HttpServletResponse response, OutputStream out, - Map unhandledParameters, Window window, WebBrowser terminalType, - Theme theme) throws IOException, MalformedURLException { - response.setContentType("text/html"); - BufferedWriter page = new BufferedWriter(new OutputStreamWriter(out)); - - page - .write("\n"); - - - - page.write("\n\nIT Mill Toolkit 5\n" + - "\n" + - "\n" + - "" + - "\n\n\n" + - " \n" + - "
" + - "
\n" + - "
console
"+ - "
IT Mill Toolkit 5 Prototype
\n" + - " \n" + - "\n"); - - - -// Theme t = theme; -// Vector themes = new Vector(); -// themes.add(t); -// while (t.getParent() != null) { -// String parentName = t.getParent(); -// t = themeSource.getThemeByName(parentName); -// themes.add(t); -// } -// for (int k = themes.size() - 1; k >= 0; k--) { -// t = (Theme) themes.get(k); -// Collection files = t.getFileNames(terminalType, Theme.MODE_AJAX); -// for (Iterator i = files.iterator(); i.hasNext();) { -// String file = (String) i.next(); -// if (file.endsWith(".css")) -// page.write("\n"); -// else if (file.endsWith(".js")) { -// page.write("\n"); -// } -// } -// -// } - - -// page.write("itmill.tmp = new itmill.Client(" -// + "document.getElementById('ajax-window')," + "\"" + appUrl -// + "/UIDL/" + "\",\"" + resourcePath -// + ((Theme) themes.get(themes.size() - 1)).getName() + "/" -// -// + "client/\",document.getElementById('ajax-wait'));\n"); - - // TODO Only current theme is registered to the client - // for (int k = themes.size() - 1; k >= 0; k--) { - // t = (Theme) themes.get(k); -// t = theme; -// String themeObjName = "itmill.themes." -// + t.getName().substring(0, 1).toUpperCase() -// + t.getName().substring(1); -// page.write(" (new " + themeObjName + "(\"" + resourcePath + t.getName() -// + "/\")).registerTo(itmill.tmp);\n"); - // } - - page.close(); - } - - /** - * 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 true if the request was handled and - * further processing should be suppressed, false - * otherwise. - * @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; - - // Removes the leading / - while (uri.startsWith("/") && uri.length() > 0) - uri = uri.substring(1); - - // Handles 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 download 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) { - } - - } - - } - - /** - * Looks for default theme JAR file. - * - * @param fileList - * @return Jar file or null if not found. - */ - private File findDefaultThemeJar(String[] fileList) { - - // Try to find the default theme JAR file based on the given path - for (int i = 0; i < fileList.length; i++) { - String path = getResourcePath(getServletContext(), fileList[i]); - File file = null; - if (path != null && (file = new File(path)).exists()) { - return file; - } - } - - // If we do not have access to individual files, create a temporary - // file from named resource. - for (int i = 0; i < fileList.length; i++) { - InputStream defaultTheme = this.getServletContext() - .getResourceAsStream(fileList[i]); - // Read the content to temporary file and return it - if (defaultTheme != null) { - return createTemporaryFile(defaultTheme, ".jar"); - } - } - - // Try to find the default theme JAR file based on file naming scheme - // NOTE: This is for backward compability with 3.0.2 and earlier. - String path = getResourcePath(getServletContext(), "/WEB-INF/lib"); - if (path != null) { - File lib = new File(path); - String[] files = lib.list(); - if (files != null) { - for (int i = 0; i < files.length; i++) { - if (files[i].toLowerCase().endsWith(".jar") - && files[i].startsWith(DEFAULT_THEME_JAR_PREFIX)) { - return new File(lib, files[i]); - } - } - } - } - - // If no file was found return null - return null; - } - - /** - * Creates a temporary file for given stream. - * - * @param stream - * the Stream to be stored into temporary file. - * @param extension - * the File type extension. - * @return the temporary File. - */ - private File createTemporaryFile(InputStream stream, String extension) { - File tmpFile; - try { - tmpFile = File.createTempFile(DEFAULT_THEME_TEMP_FILE_PREFIX, - extension); - FileOutputStream out = new FileOutputStream(tmpFile); - byte[] buf = new byte[1024]; - int bytes = 0; - while ((bytes = stream.read(buf)) > 0) { - out.write(buf, 0, bytes); - } - out.close(); - } catch (IOException e) { - System.err - .println("Failed to create temporary file for default theme: " - + e); - tmpFile = null; - } - - return tmpFile; - } - - /** - * Handles theme resource file requests. Resources supplied with the themes - * are provided by the WebAdapterServlet. - * - * @param request - * the HTTP request. - * @param response - * the HTTP response. - * @return boolean true if the request was handled and - * further processing should be suppressed, false - * otherwise. - * @throws ServletException - * if an exception has occurred that interferes with the - * servlet's normal operation. - */ - private boolean handleResourceRequest(HttpServletRequest request, - HttpServletResponse response) throws ServletException { - - // If the resource path is unassigned, initialize it - if (resourcePath == null) { - resourcePath = request.getContextPath() + request.getServletPath() - + RESOURCE_URI; - // WebSphere Application Server related fix - resourcePath = resourcePath.replaceAll("//", "/"); - } - - String resourceId = request.getPathInfo(); - - // Checks if this really is a resource request - if (resourceId == null || !resourceId.startsWith(RESOURCE_URI)) - return false; - - // Checks the resource type - resourceId = resourceId.substring(RESOURCE_URI.length()); - InputStream data = null; - // Gets theme resources - try { - data = themeSource.getResource(resourceId); - } catch (ThemeSource.ThemeException e) { - Log.info(e.getMessage()); - data = null; - } - - // Writes the response - try { - if (data != null) { - response.setContentType(FileTypeResolver - .getMIMEType(resourceId)); - - // Use default cache time for theme resources - if (this.themeCacheTime > 0) { - response.setHeader("Cache-Control", "max-age=" - + this.themeCacheTime / 1000); - response.setDateHeader("Expires", System - .currentTimeMillis() - + this.themeCacheTime); - response.setHeader("Pragma", "cache"); // Required to apply - // caching in some - // Tomcats - } - // Writes the data to client - byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; - int bytesRead = 0; - OutputStream out = response.getOutputStream(); - while ((bytesRead = data.read(buffer)) > 0) { - out.write(buffer, 0, bytesRead); - } - out.close(); - data.close(); - } else { - response.sendError(HttpServletResponse.SC_NOT_FOUND); - } - - } catch (java.io.IOException e) { - Log.info("Resource transfer failed: " + request.getRequestURI() - + ". (" + e.getMessage() + ")"); - } - - return true; - } - - /** - * Gets the variable map for the session. - * - * @param application - * @param request - * the HTTP request. - * @return the variable map. - * - */ - private static synchronized HttpVariableMap getVariableMap( - Application application, HttpServletRequest request) { - - HttpSession session = request.getSession(); - - // Gets the application to variablemap map - Map varMapMap = (Map) session.getAttribute(SESSION_ATTR_VARMAP); - if (varMapMap == null) { - varMapMap = new WeakHashMap(); - session.setAttribute(SESSION_ATTR_VARMAP, varMapMap); - } - - // Creates a variable map, if it does not exists. - HttpVariableMap variableMap = (HttpVariableMap) varMapMap - .get(application); - if (variableMap == null) { - variableMap = new HttpVariableMap(); - varMapMap.put(application, variableMap); - } - - return variableMap; - } - - /** - * Gets the current application URL from request. - * - * @param request - * the HTTP request. - * @throws MalformedURLException - * if the application is denied access to the persistent data - * store represented by the given URL. - */ - private URL getApplicationUrl(HttpServletRequest request) - throws MalformedURLException { - - URL applicationUrl; - try { - URL reqURL = new URL( - (request.isSecure() ? "https://" : "http://") - + request.getServerName() - + ((request.isSecure() && request.getServerPort() == 443) - || (!request.isSecure() && request - .getServerPort() == 80) ? "" : ":" - + request.getServerPort()) - + request.getRequestURI()); - String servletPath = request.getContextPath() - + request.getServletPath(); - if (servletPath.length() == 0 - || servletPath.charAt(servletPath.length() - 1) != '/') - servletPath = servletPath + "/"; - applicationUrl = new URL(reqURL, servletPath); - } catch (MalformedURLException e) { - Log.error("Error constructing application url " - + request.getRequestURI() + " (" + e + ")"); - throw e; - } - - return applicationUrl; - } - - /** - * Gets the existing application for given request. Looks for application - * instance for given request based on the requested URL. - * - * @param request - * the HTTP request. - * @return Application instance, or null if the URL does not map to valid - * application. - * @throws MalformedURLException - * if the application is denied access to the persistent data - * store represented by the given URL. - */ - private Application getApplication(HttpServletRequest request) - throws MalformedURLException { - - // Ensures that the session is still valid - HttpSession session = request.getSession(false); - if (session == null) - return null; - - // Gets application list for the session. - LinkedList applications = (LinkedList) session - .getAttribute(SESSION_ATTR_APPS); - if (applications == null) - return null; - - // Search for the application (using the application URI) from the list - Application application = null; - for (Iterator i = applications.iterator(); i.hasNext() - && application == null;) { - Application a = (Application) i.next(); - String aPath = a.getURL().getPath(); - String servletPath = request.getContextPath() - + request.getServletPath(); - if (servletPath.length() < aPath.length()) - servletPath += "/"; - if (servletPath.equals(aPath)) - application = a; - } - - // Removes stopped applications from the list - if (application != null && !application.isRunning()) { - applications.remove(application); - application = null; - } - - return application; - } - - /** - * Creates a new application. - * - * @param request - * the HTTP request. - * @return the New application instance. - * @throws MalformedURLException - * if the application is denied access to the persistent data - * store represented by the given URL. - * @throws InstantiationException - * if a new instance of the class cannot be instantiated. - * @throws IllegalAccessException - * if it does not have access to the property accessor method. - * @throws LicenseFileHasNotBeenRead - * if the license file has not been read. - * @throws LicenseSignatureIsInvalid - * if the license file has been changed or signature is - * otherwise invalid. - * @throws InvalidLicenseFile - * if the license file is not of correct XML format. - * @throws LicenseViolation - * - * @throws SAXException - * the Error parsing the license file. - */ - private Application createApplication(HttpServletRequest request) - throws MalformedURLException, InstantiationException, - IllegalAccessException, LicenseFileHasNotBeenRead, - LicenseSignatureIsInvalid, InvalidLicenseFile, LicenseViolation, - SAXException { - - Application application = null; - - // Gets the application url - URL applicationUrl = getApplicationUrl(request); - - // Gets application list. - HttpSession session = request.getSession(); - if (session == null) - return null; - LinkedList applications = (LinkedList) session - .getAttribute(SESSION_ATTR_APPS); - if (applications == null) { - applications = new LinkedList(); - session.setAttribute(SESSION_ATTR_APPS, applications); - HttpSessionBindingListener sessionBindingListener = new SessionBindingListener( - applications); - session.setAttribute(SESSION_BINDING_LISTENER, - sessionBindingListener); - } - - // Creates new application and start it - try { - application = (Application) this.applicationClass.newInstance(); - applications.add(application); - - // Listens to window add/removes (for web mode) - application.addListener((Application.WindowAttachListener) this); - application.addListener((Application.WindowDetachListener) this); - - // Sets locale - application.setLocale(request.getLocale()); - - // Gets application context for this session - WebApplicationContext context = (WebApplicationContext) session - .getAttribute(SESSION_ATTR_CONTEXT); - if (context == null) { - context = new WebApplicationContext(session); - session.setAttribute(SESSION_ATTR_CONTEXT, context); - } - - // Starts application and check license - initializeLicense(application); - application.start(applicationUrl, this.applicationProperties, - context); - checkLicense(application); - - } catch (IllegalAccessException e) { - Log.error("Illegal access to application class " - + this.applicationClass.getName()); - throw e; - } catch (InstantiationException e) { - Log.error("Failed to instantiate application class: " - + this.applicationClass.getName()); - throw e; - } - - return application; - } - - /** - * - * @param application - */ - private void initializeLicense(Application application) { - License license; - synchronized (licenseForApplicationClass) { - license = (License) licenseForApplicationClass.get(application - .getClass()); - if (license == null) { - license = new License(); - licenseForApplicationClass.put(application.getClass(), license); - } - } - application.setToolkitLicense(license); - } - - /** - * - * @param application - * @throws LicenseFileHasNotBeenRead - * if the license file has not been read. - * @throws LicenseSignatureIsInvalid - * if the license file has been changed or signature is - * otherwise invalid. - * @throws InvalidLicenseFile - * if the license file is not of correct XML format. - * @throws LicenseViolation - * - * @throws SAXException - * the Error parsing the license file. - */ - private void checkLicense(Application application) - throws LicenseFileHasNotBeenRead, LicenseSignatureIsInvalid, - InvalidLicenseFile, LicenseViolation, SAXException { - License license = application.getToolkitLicense(); - - if (!license.hasBeenRead()) - // Lock threads that have not yet read license - synchronized (license) { - if (!license.hasBeenRead()) { - InputStream lis; - try { - URL url = getServletContext().getResource( - "/WEB-INF/itmill-toolkit-license.xml"); - if (url == null) { - throw new RuntimeException( - "License file could not be read. " - + "You can install it to " - + "WEB-INF/itmill-toolkit-license.xml."); - } - lis = url.openStream(); - license.readLicenseFile(lis); - } catch (MalformedURLException e) { - // This should not happen - throw new RuntimeException(e); - } catch (IOException e) { - // This should not happen - throw new RuntimeException(e); - } - - // For each application class, print license description - - // once - if (!licensePrintedForApplicationClass - .containsKey(applicationClass)) { - licensePrintedForApplicationClass.put(applicationClass, - Boolean.TRUE); - if (license.shouldLimitsBePrintedOnInit()) { - System.out.println(license - .getDescription(application.getClass() - .toString())); - } - } - - // Checks license validity - try { - license.check(applicationClass, VERSION_MAJOR, - VERSION_MINOR, "IT Mill Toolkit", null); - } catch (LicenseFileHasNotBeenRead e) { - application.close(); - throw e; - } catch (LicenseSignatureIsInvalid e) { - application.close(); - throw e; - } catch (InvalidLicenseFile e) { - application.close(); - throw e; - } catch (LicenseViolation e) { - application.close(); - throw e; - } - } - } - - // Checks concurrent user limit - try { - license.checkConcurrentUsers(getNumberOfActiveUsers() + 1); - } catch (LicenseViolation e) { - application.close(); - throw e; - } - } - - /** - * Gets the number of active application-user pairs. - * - * This returns total number of all applications in the server that are - * considered to be active. For an application to be active, it must have - * been accessed less than ACTIVE_USER_REQUEST_INTERVAL ms. - * - * @return the Number of active application instances in the server. - */ - private int getNumberOfActiveUsers() { - int active = 0; - - synchronized (applicationToLastRequestDate) { - Set apps = applicationToLastRequestDate.keySet(); - long now = System.currentTimeMillis(); - for (Iterator i = apps.iterator(); i.hasNext();) { - Date lastReq = (Date) applicationToLastRequestDate - .get(i.next()); - if (now - lastReq.getTime() < ACTIVE_USER_REQUEST_INTERVAL) - active++; - } - } - - return active; - } - - /** - * Ends the application. - * - * @param request - * the HTTP request. - * @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(); - - HttpSession session = request.getSession(); - if (session != null) { - LinkedList applications = (LinkedList) session - .getAttribute(SESSION_ATTR_APPS); - if (applications != null) - applications.remove(application); - } - - response.sendRedirect(response.encodeRedirectURL(logoutUrl)); - } - - /** - * 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, Map params) throws ServletException { - - Window window = null; - - // Finds 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); - - if (window == null) { - // By default, we use main window - window = application.getMainWindow(); - } else if (!window.isVisible()) { - // Implicitly painting without actually invoking paint() - window.requestRepaintRequests(); - - // If the window is invisible send a blank page - return null; - } - } - // Creates and open new debug window for application if requested - Window debugWindow = application.getWindow(DebugWindow.WINDOW_NAME); - if (debugWindow == null) { - if (isDebugMode(params) - && WebBrowserProbe.getTerminalType(request.getSession()) - .getRenderingMode() != WebBrowser.RENDERING_MODE_AJAX) { - try { - debugWindow = new DebugWindow(application, request - .getSession(false), this); - debugWindow.setWidth(370); - debugWindow.setHeight(480); - application.addWindow(debugWindow); - } catch (Exception e) { - throw new ServletException( - "Failed to create debug window for application", e); - } - } - } else if (window != debugWindow) { - if (isDebugMode(params)) - debugWindow.requestRepaint(); - else - application.removeWindow(debugWindow); - } - - return window; - } - - /** - * Gets relative location of a theme resource. - * - * @param theme - * the Theme name. - * @param resource - * the Theme resource. - * @return External URI specifying the resource - */ - public String getResourceLocation(String theme, ThemeResource resource) { - - if (resourcePath == null) - return resource.getResourceId(); - return resourcePath + theme + "/" + resource.getResourceId(); - } - - /** - * Checks if web adapter is in debug mode. Extra output is generated to log - * when debug mode is enabled. - * - * @param parameters - * @return true if the web adapter is in debug mode. - * otherwise false. - */ - public boolean isDebugMode(Map parameters) { - if (parameters != null) { - Object[] debug = (Object[]) parameters.get("debug"); - if (debug != null && !"false".equals(debug[0].toString()) - && !"false".equals(debugMode)) - return true; - } - return "true".equals(debugMode); - } - - /** - * Returns the theme source. - * - * @return ThemeSource - */ - public ThemeSource getThemeSource() { - return themeSource; - } - - /** - * - * @param application - * @param window - */ - protected void addDirtyWindow(Application application, Window window) { - synchronized (applicationToDirtyWindowSetMap) { - HashMap dirtyWindows = (HashMap) applicationToDirtyWindowSetMap - .get(application); - if (dirtyWindows == null) { - dirtyWindows = new HashMap(); - applicationToDirtyWindowSetMap.put(application, dirtyWindows); - } - dirtyWindows.put(window, Boolean.TRUE); - } - } - - /** - * - * @param application - * @param window - */ - protected void removeDirtyWindow(Application application, Window window) { - synchronized (applicationToDirtyWindowSetMap) { - HashMap dirtyWindows = (HashMap) applicationToDirtyWindowSetMap - .get(application); - if (dirtyWindows != null) - dirtyWindows.remove(window); - } - } - - /** - * @see com.itmill.toolkit.Application.WindowAttachListener#windowAttached(Application.WindowAttachEvent) - */ - public void windowAttached(WindowAttachEvent event) { - Window win = event.getWindow(); - win.addListener((Paintable.RepaintRequestListener) this); - - // Add window to dirty window references if it is visible - // Or request the window to pass on the repaint requests - if (win.isVisible()) - addDirtyWindow(event.getApplication(), win); - else - win.requestRepaintRequests(); - - } - - /** - * @see com.itmill.toolkit.Application.WindowDetachListener#windowDetached(Application.WindowDetachEvent) - */ - public void windowDetached(WindowDetachEvent event) { - event.getWindow().removeListener( - (Paintable.RepaintRequestListener) this); - - // Adds dirty window reference for closing the window - addDirtyWindow(event.getApplication(), event.getWindow()); - } - - /** - * Receives repaint request events. - * - * @see com.itmill.toolkit.terminal.Paintable.RepaintRequestListener#repaintRequested(Paintable.RepaintRequestEvent) - */ - public void repaintRequested(RepaintRequestEvent event) { - - Paintable p = event.getPaintable(); - Application app = null; - if (p instanceof Window) - app = ((Window) p).getApplication(); - - if (app != null) - addDirtyWindow(app, ((Window) p)); - - Object lock = applicationToServerCommandStreamLock.get(app); - if (lock != null) - synchronized (lock) { - lock.notifyAll(); - } - } - - /** - * Gets the list of dirty windows in application. - * - * @param app - * @return - */ - protected Map getDirtyWindows(Application app) { - HashMap dirtyWindows; - synchronized (applicationToDirtyWindowSetMap) { - dirtyWindows = (HashMap) applicationToDirtyWindowSetMap.get(app); - } - return (Map) dirtyWindows; - } - - /** - * Removes a window from the list of dirty windows. - * - * @param app - * @param window - */ - private void windowPainted(Application app, Window window) { - removeDirtyWindow(app, window); - } - - /** - * Generates server commands stream. If the server commands are not - * requested, return false. - * - * @param request - * the HTTP request instance. - * @param response - * the HTTP response to write to. - */ - private boolean handleServerCommands(HttpServletRequest request, - HttpServletResponse response) { - - // Server commands are allways requested with certain parameter - if (request.getParameter(SERVER_COMMAND_PARAM) == null) - return false; - - // Gets the application - Application application; - try { - application = getApplication(request); - } catch (MalformedURLException e) { - return false; - } - if (application == null) - return false; - - // Creates continuous server commands stream - try { - - // Writer for writing the stream - PrintWriter w = new PrintWriter(response.getOutputStream()); - - // Prints necessary http page headers and padding - w.println(""); - for (int i = 0; i < SERVER_COMMAND_HEADER_PADDING; i++) - w.print(' '); - - // Clock for synchronizing the stream - Object lock = new Object(); - synchronized (applicationToServerCommandStreamLock) { - Object oldlock = applicationToServerCommandStreamLock - .get(application); - if (oldlock != null) - synchronized (oldlock) { - oldlock.notifyAll(); - } - applicationToServerCommandStreamLock.put(application, lock); - } - while (applicationToServerCommandStreamLock.get(application) == lock - && application.isRunning()) { - synchronized (application) { - - // Session expiration - Date lastRequest; - synchronized (applicationToLastRequestDate) { - lastRequest = (Date) applicationToLastRequestDate - .get(application); - } - if (lastRequest != null - && lastRequest.getTime() - + request.getSession() - .getMaxInactiveInterval() * 1000 < System - .currentTimeMillis()) { - - // Session expired, close application - application.close(); - } else { - - // Application still alive - keep updating windows - Map dws = getDirtyWindows(application); - if (dws != null && !dws.isEmpty()) { - - // For one of the dirty windows (in each - // application) - // request redraw - Window win = (Window) dws.keySet().iterator() - .next(); - w - .println(""); - - removeDirtyWindow(application, win); - - // Windows that are closed immediately are "painted" - // now - if (win.getApplication() == null - || !win.isVisible()) - win.requestRepaintRequests(); - } - } - } - - // Sends the generated commands and newline immediately to - // browser - // TODO why space in here? why not plain ln? - w.println(" "); - w.flush(); - response.flushBuffer(); - - synchronized (lock) { - try { - lock.wait(SERVER_COMMAND_STREAM_MAINTAIN_PERIOD); - } catch (InterruptedException ignored) { - } - } - } - } catch (IOException ignore) { - - // In case of an Exceptions the server command stream is - // terminated - synchronized (applicationToServerCommandStreamLock) { - if (applicationToServerCommandStreamLock.get(application) == application) - applicationToServerCommandStreamLock.remove(application); - } - } - - return true; - } - - /** - * - * SessionBindingListener performs Application cleanups after sessions are - * expired. For each session exists one SessionBindingListener. It contains - * references to all applications related to single session. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 4.0 - */ - - private class SessionBindingListener implements HttpSessionBindingListener { - private LinkedList applications; - - /** - * - * @param applications - */ - protected SessionBindingListener(LinkedList applications) { - this.applications = applications; - } - - /** - * @see javax.servlet.http.HttpSessionBindingListener#valueBound(HttpSessionBindingEvent) - */ - public void valueBound(HttpSessionBindingEvent arg0) { - // We are not interested in bindings - } - - /** - * @see javax.servlet.http.HttpSessionBindingListener#valueUnbound(HttpSessionBindingEvent) - */ - public void valueUnbound(HttpSessionBindingEvent event) { - // If the binding listener is unbound from the session, the - // session must be closing - if (event.getName().equals(SESSION_BINDING_LISTENER)) { - // Close all applications related to given session - Object[] apps = applications.toArray(); - for (int i = 0; i < apps.length; i++) { - if (apps[i] != null) { - // Close application - ((Application) apps[i]).close(); - - // Stops application server commands stream - Object lock = applicationToServerCommandStreamLock - .get(apps[i]); - if (lock != null) - synchronized (lock) { - lock.notifyAll(); - } - - // Remove application from hashmaps - synchronized (applicationToServerCommandStreamLock) { - applicationToServerCommandStreamLock - .remove(apps[i]); - } - synchronized (applicationToDirtyWindowSetMap) { - applicationToDirtyWindowSetMap.remove(apps[i]); - } - synchronized (applicationToLastRequestDate) { - applicationToLastRequestDate.remove(apps[i]); - } - synchronized (applicationToAjaxAppMgrMap) { - applicationToAjaxAppMgrMap.remove(apps[i]); - } - // Remove application from applications list - applications.remove(apps[i]); - } - } - } - } - } - - /** - * Implementation of ParameterHandler.ErrorEvent interface. - */ - public class ParameterHandlerErrorImpl implements - ParameterHandler.ErrorEvent { - - private ParameterHandler owner; - - private Throwable throwable; - - /** - * - * @param owner - * @param throwable - */ - private ParameterHandlerErrorImpl(ParameterHandler owner, - Throwable throwable) { - this.owner = owner; - this.throwable = throwable; - } - - /** - * Gets the contained throwable. - * - * @see com.itmill.toolkit.terminal.Terminal.ErrorEvent#getThrowable() - */ - public Throwable getThrowable() { - return this.throwable; - } - - /** - * Gets the source ParameterHandler. - * - * @see com.itmill.toolkit.terminal.ParameterHandler.ErrorEvent#getParameterHandler() - */ - public ParameterHandler getParameterHandler() { - return this.owner; - } - - } - - /** - * 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; - } - - /** - * Gets the contained throwable. - * - * @see com.itmill.toolkit.terminal.Terminal.ErrorEvent#getThrowable() - */ - public Throwable getThrowable() { - return this.throwable; - } - - /** - * Gets the source URIHandler. - * - * @see com.itmill.toolkit.terminal.URIHandler.ErrorEvent#getURIHandler() - */ - public URIHandler getURIHandler() { - return this.owner; - } - } - - /** - * Gets AJAX application manager for an application. - * - * If this application has not been running in ajax mode before, new manager - * is created and web adapter stops listening to changes. - * - * @param application - * @return AJAX Application Manager - */ - private AjaxApplicationManager getApplicationManager(Application application) { - AjaxApplicationManager mgr = (AjaxApplicationManager) applicationToAjaxAppMgrMap - .get(application); - - // This application is going from Web to AJAX mode, create new manager - if (mgr == null) { - // Creates new manager - mgr = new AjaxApplicationManager(application); - applicationToAjaxAppMgrMap.put(application, mgr); - - // Stops sending changes to this servlet because manager will take - // control - application.removeListener((Application.WindowAttachListener) this); - application.removeListener((Application.WindowDetachListener) this); - - // Deregister all window listeners - for (Iterator wins = application.getWindows().iterator(); wins - .hasNext();) - ((Window) wins.next()) - .removeListener((Paintable.RepaintRequestListener) this); - - // Manager takes control over the application - mgr.takeControl(); - } - - return mgr; - } - - /** - * Gets resource path using different implementations. Required fo - * supporting different servlet container implementations (application - * servers). - * - * @param servletContext - * @param path - * the resource path. - * @return the resource path. - */ - protected static String getResourcePath(ServletContext servletContext, - String path) { - String resultPath = null; - resultPath = servletContext.getRealPath(path); - if (resultPath != null) { - return resultPath; - } else { - try { - URL url = servletContext.getResource(path); - resultPath = url.getFile(); - } catch (Exception e) { - // ignored - } - } - return resultPath; - } - -} \ No newline at end of file diff --git a/src/com/itmill/toolkit/terminal/web/CollectionThemeSource.java b/src/com/itmill/toolkit/terminal/web/CollectionThemeSource.java deleted file mode 100644 index 047b787bf5..0000000000 --- a/src/com/itmill/toolkit/terminal/web/CollectionThemeSource.java +++ /dev/null @@ -1,210 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import java.io.InputStream; -import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -/** - * Theme source for consisting of collection of other theme sources. This class - * is used to implement the retrieval of themes from multiple sources. Also this - * class implements the inheritance of themes by first retrieving the relevant - * parent theme information. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ -public class CollectionThemeSource implements ThemeSource { - - private List sources = new LinkedList(); - - /** - * Gets the name of the ThemeSource. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getName() - */ - public String getName() { - return "THEMES"; - } - - /** - * Gets the XSL stream for the specified theme and web-browser type. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getXSLStreams(Theme, - * WebBrowser) - */ - public Collection getXSLStreams(Theme theme, WebBrowser type) - throws ThemeException { - Collection xslFiles = new LinkedList(); - - // Adds parent theme XSL - xslFiles.addAll(this.getParentXSLStreams(theme, type)); - - // Adds theme XSL, Handle subdirectories: return the first match - for (Iterator i = this.sources.iterator(); i.hasNext();) { - ThemeSource source = (ThemeSource) i.next(); - if (source.getThemes().contains(theme)) - xslFiles.addAll(source.getXSLStreams(theme, type)); - } - - return xslFiles; - } - - /** - * - * @param theme - * @param type - * @return - * @throws ThemeException - * If the resource is not found or there was some problem - * finding the resource. - */ - private Collection getParentXSLStreams(Theme theme, WebBrowser type) - throws ThemeException { - Collection xslFiles = new LinkedList(); - String parentName = theme.getParent(); - if (parentName != null) { - Theme parent = this.getThemeByName(parentName); - if (parent != null) { - xslFiles.addAll(this.getXSLStreams(parent, type)); - } else { - throw new ThemeSource.ThemeException( - "Parent theme not found for name: " + parentName); - } - } - return xslFiles; - } - - /** - * Gets the last modification time, used to reload theme on changes. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getModificationTime() - */ - public long getModificationTime() { - long modTime = 0; - for (Iterator i = this.sources.iterator(); i.hasNext();) { - long t = ((ThemeSource) i.next()).getModificationTime(); - if (t > modTime) - modTime = t; - } - return modTime; - } - - /** - * Gets the input stream for the resource with the specified resource id. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getResource(String) - */ - public InputStream getResource(String resourceId) throws ThemeException { - - // Resolves theme name and resource name - int delim = resourceId.indexOf("/"); - String subResourceId = ""; - String themeName = ""; - if (delim >= 0 && delim < resourceId.length() - 1) { - subResourceId = resourceId.substring(delim + 1); - themeName = resourceId.substring(0, delim); - } - - // Gets the list of themes to look for the resource - List themes = new LinkedList(); - while (themeName != null && themeName.length() > 0) { - Theme t = this.getThemeByName(themeName); - if (t != null) - themes.add(themeName); - themeName = t.getParent(); - } - - // Iterate all themes in list - for (Iterator ti = themes.iterator(); ti.hasNext();) { - String name = (String) ti.next(); - String resource = name + "/" + subResourceId; - // Search all sources - for (Iterator i = this.sources.iterator(); i.hasNext();) { - try { - InputStream in = ((ThemeSource) i.next()) - .getResource(resource); - if (in != null) - return in; - } catch (ThemeException e) { - // Ignore and continue to next source - } - } - } - - throw new ThemeException("Theme resource not found:" + subResourceId - + " in themes " + themes); - } - - /** - * Gets the list of themes in the theme source. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getThemes() - */ - public Collection getThemes() { - Collection themes = new LinkedList(); - for (Iterator i = this.sources.iterator(); i.hasNext();) { - Collection c = ((ThemeSource) i.next()).getThemes(); - themes.addAll(c); - } - return themes; - } - - /** - * Gets the theme instance by name. - * - * @param name - * the theme name. - * @see com.itmill.toolkit.terminal.web.ThemeSource#getThemeByName(String) - */ - public Theme getThemeByName(String name) { - for (Iterator i = this.sources.iterator(); i.hasNext();) { - Theme t = ((ThemeSource) i.next()).getThemeByName(name); - if (t != null) - return t; - } - return null; - } - - /** - * Adds new theme source to this collection. - * - * @param source - * the Theme source to be added. - */ - public void add(ThemeSource source) { - this.sources.add(source); - } - -} diff --git a/src/com/itmill/toolkit/terminal/web/DebugWindow.java b/src/com/itmill/toolkit/terminal/web/DebugWindow.java deleted file mode 100644 index 42d9f11fe5..0000000000 --- a/src/com/itmill/toolkit/terminal/web/DebugWindow.java +++ /dev/null @@ -1,446 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; - -import javax.servlet.http.HttpSession; - -import com.itmill.toolkit.Application; -import com.itmill.toolkit.data.util.BeanItem; -import com.itmill.toolkit.data.util.MethodProperty; -import com.itmill.toolkit.terminal.FileResource; -import com.itmill.toolkit.ui.*; - -/** - * This class provides a debugging window where one may view the UIDL of the - * current window, or in a tabset the UIDL of an active frameset. - * - * It is primarily intended for creating and debugging themes. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ -public class DebugWindow extends Window { - - protected static String WINDOW_NAME = "debug"; - - private Application debuggedApplication; - - private HashMap rawUIDL = new HashMap(); - - private ApplicationServlet servlet; - - private HttpSession session; - - private TabSheet tabs = new TabSheet(); - - private Select themeSelector; - - private Label applicationInfo = new Label("", Label.CONTENT_XHTML); - - /** - * Creates a new debug window for an application. - * - * @param debuggedApplication - * the Application to be debugged. - * @param session - * the Session to be debugged. - * @param servlet - * the Servlet to be debugged. - */ - protected DebugWindow(Application debuggedApplication, HttpSession session, - ApplicationServlet servlet) { - - super("Debug window"); - setName(WINDOW_NAME); - setServlet(servlet); - setSession(session); - setBorder(Window.BORDER_NONE); - - // Creates control buttons - OrderedLayout controls = new OrderedLayout( - OrderedLayout.ORIENTATION_HORIZONTAL); - controls.addComponent(new Button("Restart Application", this, - "restartApplication")); - controls - .addComponent(new Button("Clear Session", this, "clearSession")); - Collection themes = servlet.getThemeSource().getThemes(); - Collection names = new LinkedList(); - for (Iterator i = themes.iterator(); i.hasNext();) { - names.add(((Theme) i.next()).getName()); - } - - // Creates theme selector - themeSelector = new Select("Application Theme", names); - themeSelector.setWriteThrough(false); - - // Terminal type editor - Label terminal = new Label("

Terminal Information

", - Label.CONTENT_XHTML); - Form browser = new Form(); - browser.setItemDataSource(new BeanItem(WebBrowserProbe - .getTerminalType(session))); - browser.removeItemProperty("class"); - browser.replaceWithSelect("javaScriptVersion", - WebBrowser.JAVASCRIPT_VERSIONS, WebBrowser.JAVASCRIPT_VERSIONS); - browser.replaceWithSelect("markupVersion", WebBrowser.MARKUP_VERSIONS, - WebBrowser.MARKUP_VERSIONS); - browser.setWriteThrough(false); - Button setbrowser = new Button("Set terminal information", browser, - "commit"); - setbrowser.dependsOn(browser); - - // Arrange the UI in tabsheet - TabSheet infoTabs = new TabSheet(); - addComponent(infoTabs); - - OrderedLayout appInfo = new OrderedLayout(); - infoTabs.addTab(appInfo, "Application", null); - appInfo.addComponent(applicationInfo); - appInfo.addComponent(controls); - appInfo.addComponent(themeSelector); - appInfo.addComponent(new Button("Change theme", this, "commitTheme")); - - OrderedLayout winInfo = new OrderedLayout(); - infoTabs.addTab(winInfo, "Windows", null); - winInfo.addComponent(tabs); - winInfo.addComponent(new Button("Save UIDL", this, "saveUIDL")); - - OrderedLayout termInfo = new OrderedLayout(); - infoTabs.addTab(termInfo, "Terminal", null); - termInfo.addComponent(terminal); - termInfo.addComponent(browser); - termInfo.addComponent(setbrowser); - - // Sets the debugged application - setDebuggedApplication(debuggedApplication); - - } - - /** - * - * @param caption - * @param keys - * @param names - * @return - */ - protected Select createSelect(String caption, Object[] keys, String[] names) { - Select s = new Select(caption); - s.addContainerProperty("name", String.class, ""); - for (int i = 0; i < keys.length; i++) { - s.addItem(keys[i]).getItemProperty("name").setValue(names[i]); - } - s.setItemCaptionPropertyId("name"); - return s; - } - - /** - * Saves the UIDL. - */ - public void saveUIDL() { - - synchronized (rawUIDL) { - - String currentUIDL = (String) rawUIDL.get(tabs.getSelectedTab()); - - if (currentUIDL == null) - return; - - DateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss"); - File file = new File("/uidl-debug" - + df.format(new Date(System.currentTimeMillis())) + ".xml"); - try { - BufferedWriter out = new BufferedWriter(new OutputStreamWriter( - new FileOutputStream(file))); - out.write(currentUIDL); - out.close(); - - // Open the UIDL also - open(new FileResource(file, this.getApplication())); - Log.info("UIDL written to file " + file); - } catch (FileNotFoundException e) { - Log.info("Failed to write debug to " + file + ": " + e); - } catch (IOException e) { - Log.info("Failed to write debug to " + file + ": " + e); - } - } - } - - /** - * Commits the theme. - * - */ - public void commitTheme() { - themeSelector.commit(); - } - - /** - * Clears the session. - */ - public void clearSession() { - session.invalidate(); - } - - /** - * Restarts the Application. - * - */ - public void restartApplication() { - if (debuggedApplication != null) - debuggedApplication.close(); - } - - /** - * - * @param window - * @param uidl - */ - protected void setWindowUIDL(Window window, String uidl) { - String caption = "UIDL:" + window.getName(); - synchronized (tabs) { - for (Iterator i = tabs.getComponentIterator(); i.hasNext();) { - Component c = (Component) i.next(); - if (tabs.getTabCaption(c).equals(caption)) { - ((Label) c).setValue(getHTMLFormattedUIDL(caption, uidl)); - ((Label) c).setContentMode(Label.CONTENT_XHTML); - rawUIDL.put(c, uidl); - caption = null; - } - } - - // Add new tab - if (caption != null) { - Label l = new Label(getHTMLFormattedUIDL(caption, uidl)); - l.setContentMode(Label.CONTENT_XHTML); - rawUIDL.put(l, uidl); - tabs.addTab(l, caption, null); - } - } - } - - /** - * - * @param caption - * @param uidl - * @return - */ - protected String getHTMLFormattedUIDL(String caption, String uidl) { - StringBuffer sb = new StringBuffer(); - - // Print formatted UIDL with errors embedded - // Perl5Util util = new Perl5Util(); - - int row = 0; - int prev = 0; - int index = 0; - boolean lastLineWasEmpty = false; - - sb - .append(""); - - if (caption != null) - sb.append("\n"); - - boolean unfinished = true; - while (unfinished) { - row++; - - // Get individual line - index = uidl.indexOf('\n', prev); - String line; - if (index < 0) { - unfinished = false; - line = uidl.substring(prev); - } else { - line = uidl.substring(prev, index); - prev = index + 1; - } - - // Escape the XML - line = WebPaintTarget.escapeXML(line); - - // Code beautification : Comment lines - line = replaceAll(line, "<!--", - "<!--"); - line = replaceAll(line, "-->", "-->"); - - while (line.length() > 0 && line.charAt(0) == ' ') { - line = line.substring(1); - } - boolean isEmpty = (line.length() == 0 || line.equals("\r")); - line = " " + line; - - if (!(isEmpty && lastLineWasEmpty)) - sb - .append(" 4 ? " BGCOLOR=\"#eeeeff\"" : "") - + ">" - + "\n"); - - lastLineWasEmpty = isEmpty; - - } - - sb.append("
" - + "" + caption + "
" - + String.valueOf(row) + " " + line - + "
\n"); - - return sb.toString(); - } - - /** - * Replaces the characters in a substring of this String with - * characters in the specified String. The substring begins - * at the specified start and extends to the character at - * index end - 1 or to the end of the String - * if no such character exists. - *

- * First the characters in the substring are removed and then the specified - * String is inserted at start. (The - * StringBuffer will be lengthened to accommodate the - * specified String if necessary.) - *

- *

- * NOTE: This operation is slow. - *

- * - * @param text - * @param start - * the beginning index, inclusive. - * @param end - * the ending index, exclusive. - * @param str - * the String that will replace previous contents. - * @return This string buffer. - */ - protected static String replace(String text, int start, int end, String str) { - return new StringBuffer(text).replace(start, end, str).toString(); - } - - /** - * - * @param text - * @param oldStr - * @param newStr - * @return - */ - protected static String replaceAll(String text, String oldStr, String newStr) { - StringBuffer sb = new StringBuffer(text); - - int newStrLen = newStr.length(); - int oldStrLen = oldStr.length(); - if (oldStrLen <= 0) - return text; - - int i = 0; - while (i <= sb.length() - oldStrLen) { - if (sb.substring(i, i + oldStrLen).equals(oldStr)) { - sb.replace(i, i + oldStrLen, newStr); - i += newStrLen; - } else { - i++; - } - } - return sb.toString(); - } - - /** - * Sets the application. - * - * @param application - * the application to set. - */ - protected void setDebuggedApplication(Application application) { - this.debuggedApplication = application; - if (application != null) { - applicationInfo.setValue("

Application Class

" - + application.getClass().getName()); - themeSelector.setPropertyDataSource(new MethodProperty(application, - "theme")); - } - } - - /** - * Returns the servlet. - * - * @return the WebAdapterServlet. - */ - protected ApplicationServlet getServlet() { - return servlet; - } - - /** - * Returns the session. - * - * @return the HttpSession. - */ - protected HttpSession getSession() { - return session; - } - - /** - * Sets the servlet. - * - * @param servlet - * the servlet to set. - */ - protected void setServlet(ApplicationServlet servlet) { - this.servlet = servlet; - } - - /** - * Sets the session. - * - * @param session - * the session to set. - */ - protected void setSession(HttpSession session) { - this.session = session; - } - -} diff --git a/src/com/itmill/toolkit/terminal/web/DirectoryThemeSource.java b/src/com/itmill/toolkit/terminal/web/DirectoryThemeSource.java deleted file mode 100644 index 7130d4c6c3..0000000000 --- a/src/com/itmill/toolkit/terminal/web/DirectoryThemeSource.java +++ /dev/null @@ -1,312 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedList; - -/** - * Theme source for reading themes from a directory on the Filesystem. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ -public class DirectoryThemeSource implements ThemeSource { - - private File path; - - private Theme theme; - - private ApplicationServlet webAdapterServlet; - - /** - * Collection of subdirectory entries. - */ - private Collection subdirs = new LinkedList(); - - /** - * Creates a new instance of ThemeRepository by reading the themes from a - * local directory. - * - * @param path - * the Path to the source directory . - * @param webAdapterServlet - * @throws ThemeException - * If the resource is not found or there was some problem - * finding the resource. - * @throws FileNotFoundException - * if no theme files are found. - * @throws IOException - * if the writing failed due to input/output error. - */ - public DirectoryThemeSource(File path, ApplicationServlet webAdapterServlet) - throws ThemeException, FileNotFoundException, IOException { - - this.path = path; - this.theme = null; - this.webAdapterServlet = webAdapterServlet; - - if (!this.path.isDirectory()) - throw new java.io.FileNotFoundException( - "Theme path must be a directory ('" + this.path + "')"); - - // Loads description file - File description = new File(path, Theme.DESCRIPTIONFILE); - if (description.exists()) { - try { - this.theme = new Theme(description); - } catch (Exception e) { - throw new ThemeException("ServletThemeSource: Failed to load '" - + path, e); - } - - // Debug info - if (webAdapterServlet.isDebugMode(null)) { - Log.info("Added DirectoryThemeSource: " + this.path); - } - - } else { - // There was no description file found. - // Handle subdirectories recursively - File[] files = this.path.listFiles(); - for (int i = 0; i < files.length; i++) { - if (files[i].isDirectory()) { - this.subdirs.add(new DirectoryThemeSource(files[i], - webAdapterServlet)); - } else if (files[i].getName().toLowerCase().endsWith(".jar")) { - this.subdirs.add(new JarThemeSource(files[i], - webAdapterServlet, "")); - } - } - - if (this.subdirs.isEmpty()) { - if (webAdapterServlet.isDebugMode(null)) { - Log - .debug("DirectoryThemeSource: Ignoring empty directory: " - + path); - } - } - } - } - - /** - * Gets the XSL stream for the specified theme and web-browser type. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getXSLStreams(Theme, - * WebBrowser) - */ - public Collection getXSLStreams(Theme theme, WebBrowser type) - throws ThemeException { - Collection xslFiles = new LinkedList(); - - // If this directory contains a theme - // return XSL from this theme - if (this.theme != null) { - - if (webAdapterServlet.isDebugMode(null)) { - Log.info("DirectoryThemeSource: Loading XSL from: " + theme); - } - - // Reloads the description file - File description = new File(path, Theme.DESCRIPTIONFILE); - if (description.exists()) { - try { - this.theme = new Theme(description); - } catch (IOException e) { - throw new ThemeException( - "Failed to reload theme description" + e); - } - } - - Collection fileNames = theme.getFileNames(type, Theme.MODE_HTML); - - // Adds all XSL file streams - for (Iterator i = fileNames.iterator(); i.hasNext();) { - File f = new File(this.path, (String) i.next()); - if (f.getName().endsWith(".xsl")) - try { - xslFiles.add(new XSLStream(f.getName(), - new FileInputStream(f))); - } catch (FileNotFoundException e) { - throw new ThemeException("XSL File not found: " + f); - } - } - - } else { - - // Handles subdirectories: return the first match - for (Iterator i = this.subdirs.iterator(); i.hasNext();) { - ThemeSource source = (ThemeSource) i.next(); - if (source.getThemes().contains(theme)) - xslFiles.addAll(source.getXSLStreams(theme, type)); - } - } - - // Returns the concatenated stream - return xslFiles; - - } - - /** - * Gets the last modification time, used to reload theme on changes. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getModificationTime() - */ - public long getModificationTime() { - - long modTime = 0; - - // If this directory contains a theme - // returns XSL from this theme - if (this.theme != null) { - - // Gets modification time of the description file - modTime = new File(this.path, Theme.DESCRIPTIONFILE).lastModified(); - - // Gets modification time of the themes directory itself - if (this.path.lastModified() > modTime) { - modTime = this.path.lastModified(); - } - - // Check modification time for all files - Collection fileNames = theme.getFileNames(); - for (Iterator i = fileNames.iterator(); i.hasNext();) { - File f = new File(this.path, (String) i.next()); - if (f.lastModified() > modTime) { - modTime = f.lastModified(); - } - } - } else { - // Handles subdirectories - for (Iterator i = this.subdirs.iterator(); i.hasNext();) { - ThemeSource source = (ThemeSource) i.next(); - long t = source.getModificationTime(); - if (t > modTime) - modTime = t; - } - } - - return modTime; - - } - - /** - * Gets the input stream for the resource with the specified resource id. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getResource(String) - */ - public InputStream getResource(String resourceId) - throws ThemeSource.ThemeException { - - // If this directory contains a theme - // return resource from this theme - if (this.theme != null) { - - try { - return new FileInputStream(new File(this.path, resourceId)); - } catch (FileNotFoundException e) { - throw new ThemeSource.ThemeException("Resource " + resourceId - + " not found."); - } - - } else { - int delim = resourceId.indexOf("/"); - String subResourceName = ""; - if (delim < resourceId.length() - 1) - subResourceName = resourceId.substring(delim + 1); - String subSourceName = resourceId.substring(0, delim); - for (Iterator i = this.subdirs.iterator(); i.hasNext();) { - ThemeSource source = (ThemeSource) i.next(); - if (source.getName().equals(subSourceName)) { - return source.getResource(subResourceName); - } - } - } - - throw new ThemeSource.ThemeException("Resource " + resourceId - + " not found."); - - } - - /** - * Gets the list of themes in the theme source. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getThemes() - */ - public Collection getThemes() { - Collection themes = new LinkedList(); - if (this.theme != null) { - themes.add(this.theme); - } else { - for (Iterator i = this.subdirs.iterator(); i.hasNext();) { - ThemeSource source = (ThemeSource) i.next(); - themes.addAll(source.getThemes()); - } - - } - return themes; - } - - /** - * Gets the name of the ThemeSource. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getName() - */ - public String getName() { - if (this.theme != null) { - return this.theme.getName(); - } else { - return this.path.getName(); - } - } - - /** - * Gets the Theme instance by name. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getThemeByName(String) - */ - public Theme getThemeByName(String name) { - Collection themes = this.getThemes(); - for (Iterator i = themes.iterator(); i.hasNext();) { - Theme t = (Theme) i.next(); - if (name != null && name.equals(t.getName())) - return t; - } - return null; - } - -} diff --git a/src/com/itmill/toolkit/terminal/web/HttpUploadStream.java b/src/com/itmill/toolkit/terminal/web/HttpUploadStream.java deleted file mode 100644 index deaeaeafec..0000000000 --- a/src/com/itmill/toolkit/terminal/web/HttpUploadStream.java +++ /dev/null @@ -1,114 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import java.io.InputStream; - -/** - * WebAdapter implementation of the UploadStream interface. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ -public class HttpUploadStream implements - com.itmill.toolkit.terminal.UploadStream { - - /** - * Holds value of property variableName. - */ - private String streamName; - - private String contentName; - - private String contentType; - - /** - * Holds value of property variableValue. - */ - private InputStream stream; - - /** - * Creates a new instance of UploadStreamImpl - * - * @param name - * the name of the stream. - * @param stream - * the input stream. - * @param contentName - * the name of the content. - * @param contentType - * the type of the content. - */ - public HttpUploadStream(String name, InputStream stream, - String contentName, String contentType) { - this.streamName = name; - this.stream = stream; - this.contentName = contentName; - this.contentType = contentType; - } - - /** - * Gets the name of the stream. - * - * @return the name of the stream. - */ - public String getStreamName() { - return this.streamName; - } - - /** - * Gets the input stream. - * - * @return the Input stream. - */ - public InputStream getStream() { - return this.stream; - } - - /** - * Gets the input stream content type. - * - * @return the content type of the input stream. - */ - public String getContentType() { - return this.contentType; - } - - /** - * Gets the stream content name. Stream content name usually differs from - * the actual stream name. It is used to identify the content of the stream. - * - * @return the Name of the stream content. - */ - public String getContentName() { - return this.contentName; - } -} diff --git a/src/com/itmill/toolkit/terminal/web/HttpVariableMap.java b/src/com/itmill/toolkit/terminal/web/HttpVariableMap.java deleted file mode 100644 index a22d1a37f0..0000000000 --- a/src/com/itmill/toolkit/terminal/web/HttpVariableMap.java +++ /dev/null @@ -1,791 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import com.itmill.toolkit.terminal.SystemError; -import com.itmill.toolkit.terminal.Terminal; -import com.itmill.toolkit.terminal.UploadStream; -import com.itmill.toolkit.terminal.VariableOwner; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.List; -import java.util.StringTokenizer; -import java.util.Enumeration; -import java.util.WeakHashMap; -import java.io.IOException; -import java.lang.ref.WeakReference; - -import javax.servlet.http.HttpServletRequest; -import java.util.LinkedList; -import java.util.Iterator; - -/** - * Class implementing the variable mappings. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ -public class HttpVariableMap { - - // Id <-> (Owner,Name) mapping - private Map idToNameMap = new HashMap(); - - private Map idToTypeMap = new HashMap(); - - private Map idToOwnerMap = new HashMap(); - - private Map idToValueMap = new HashMap(); - - private Map ownerToNameToIdMap = new WeakHashMap(); - - private Object mapLock = new Object(); - - // Id generator - private long lastId = 0; - - /** - * Converts the string to a supported class. - * - * @param type - * @param value - * @throws java.lang.ClassCastException - */ - private static Object convert(Class type, String value) - throws java.lang.ClassCastException { - try { - - // Boolean typed variables - if (type.equals(Boolean.class)) - return new Boolean(!(value.equals("") || value.equals("false"))); - - // Integer typed variables - if (type.equals(Integer.class)) - return new Integer(value.trim()); - - // String typed variables - if (type.equals(String.class)) - return value; - - throw new ClassCastException("Unsupported type: " + type.getName()); - } catch (NumberFormatException e) { - return null; - } - } - - /** - * Registers a new variable. - * - * @param name - * the name of the variable. - * @param type - * @param value - * @param owner - * the Listener for variable changes. - * - * @return id to assigned for this variable. - */ - public String registerVariable(String name, Class type, Object value, - VariableOwner owner) { - - // Checks that the type of the class is supported - if (!(type.equals(Boolean.class) || type.equals(Integer.class) - || type.equals(String.class) || type.equals(String[].class) || type - .equals(UploadStream.class))) - throw new SystemError("Unsupported variable type: " - + type.getClass()); - - synchronized (mapLock) { - - // Checks if the variable is already mapped - HashMap nameToIdMap = (HashMap) ownerToNameToIdMap.get(owner); - if (nameToIdMap == null) { - nameToIdMap = new HashMap(); - ownerToNameToIdMap.put(owner, nameToIdMap); - } - String id = (String) nameToIdMap.get(name); - - if (id == null) { - // Generates new id and register it - id = "v" + String.valueOf(++lastId); - nameToIdMap.put(name, id); - idToOwnerMap.put(id, new WeakReference(owner)); - idToNameMap.put(id, name); - idToTypeMap.put(id, type); - } - - idToValueMap.put(id, value); - - return id; - } - } - - /** - * Unregisters the variable. - * - * @param name - * the name of the variable. - * @param owner - * the Listener for variable changes. - */ - public void unregisterVariable(String name, VariableOwner owner) { - - synchronized (mapLock) { - - // Gets the id - HashMap nameToIdMap = (HashMap) ownerToNameToIdMap.get(owner); - if (nameToIdMap == null) - return; - String id = (String) nameToIdMap.get(name); - if (id != null) - return; - - // Removes all the mappings - nameToIdMap.remove(name); - if (nameToIdMap.isEmpty()) - ownerToNameToIdMap.remove(owner); - idToNameMap.remove(id); - idToTypeMap.remove(id); - idToValueMap.remove(id); - idToOwnerMap.remove(id); - - } - } - - /** - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - private class ParameterContainer { - - /** - * Constructs the mapping: listener to set of listened parameter names. - */ - private HashMap parameters = new HashMap(); - - /** - * Parameter values. - */ - private HashMap values = new HashMap(); - - /** - * Multipart parser used for parsing the request. - */ - private ServletMultipartRequest parser = null; - - /** - * Name - Value mapping of parameters that are not variables. - */ - private HashMap nonVariables = new HashMap(); - - /** - * Creates a new parameter container and parse the parameters from the - * request using GET, POST and POST/MULTIPART parsing. - * - * @param req - * the HTTP request. - * @throws IOException - * if the writing failed due to input/output error. - */ - public ParameterContainer(HttpServletRequest req) throws IOException { - // Parse GET / POST parameters - for (Enumeration e = req.getParameterNames(); e.hasMoreElements();) { - String paramName = (String) e.nextElement(); - String[] paramValues = req.getParameterValues(paramName); - addParam(paramName, paramValues); - } - - // Parse multipart variables - try { - parser = new ServletMultipartRequest(req, - MultipartRequest.MAX_READ_BYTES); - } catch (IllegalArgumentException ignored) { - parser = null; - } - - if (parser != null) { - for (Enumeration e = parser.getFileParameterNames(); e - .hasMoreElements();) { - String paramName = (String) e.nextElement(); - addParam(paramName, null); - } - for (Enumeration e = parser.getParameterNames(); e - .hasMoreElements();) { - String paramName = (String) e.nextElement(); - Enumeration val = parser.getURLParameters(paramName); - - // Create a linked list from enumeration to calculate - // elements - LinkedList l = new LinkedList(); - while (val.hasMoreElements()) - l.addLast(val.nextElement()); - - // String array event constructor - String[] s = new String[l.size()]; - Iterator i = l.iterator(); - for (int j = 0; j < s.length; j++) - s[j] = (String) i.next(); - - addParam(paramName, s); - } - } - - } - - /** - * Adds the parameter to container. - * - * @param name - * the name of the parameter. - * @param value - */ - private void addParam(String name, String[] value) { - - // Support name="set:name=value" value="ignored" notation - if (name.startsWith("set:")) { - int equalsIndex = name.indexOf('='); - value[0] = name.substring(equalsIndex + 1, name.length()); - name = name.substring(4, equalsIndex); - String[] curVal = (String[]) values.get(name); - if (curVal != null) { - String[] newVal = new String[1 + curVal.length]; - newVal[curVal.length] = value[0]; - for (int i = 0; i < curVal.length; i++) - newVal[i] = curVal[i]; - value = newVal; - - // Special case - if the set:-method is used for - // declaring array of length 2, where either of the - // following conditions are true: - // - the both items are the same - // - the both items have the same length and - // - the items only differ on last character - // - second last character is '.' - // - last char of one string is 'x' and other is 'y' - // Browser is unporposely modifying the name. - if (value.length == 2 - && value[0].length() == value[1].length()) { - boolean same = true; - for (int i = 0; i < value[0].length() - 1 && same; i++) - if (value[0].charAt(i) != value[1].charAt(i)) - same = false; - if (same - && ((value[0].charAt(value[0].length() - 1) == 'x' && value[1] - .charAt(value[1].length() - 1) == 'y') || (value[0] - .charAt(value[0].length() - 1) == 'y' && value[1] - .charAt(value[1].length() - 1) == 'x'))) { - value = new String[] { value[0].substring(0, - value[1].length() - 2) }; - } else if (same && value[0].equals(value[1])) - value = new String[] { value[0] }; - } - - // Special case - if the set:-method is used for - // declaring array of length 3, where all of the - // following conditions are true: - // - two last items have the same length - // - the first item is 2 chars shorter - // - the longer items only differ on last character - // - the shortest item is a prefix of the longer ones - // - second last character of longer ones is '.' - // - last char of one long string is 'x' and other is 'y' - // Browser is unporposely modifying the name. (Mozilla, - // Firefox, ..) - if (value.length == 3 - && value[1].length() == value[2].length() - && value[0].length() + 2 == value[1].length()) { - boolean same = true; - for (int i = 0; i < value[1].length() - 1 && same; i++) - if (value[2].charAt(i) != value[1].charAt(i)) - same = false; - for (int i = 0; i < value[0].length() && same; i++) - if (value[0].charAt(i) != value[1].charAt(i)) - same = false; - if (same - && (value[2].charAt(value[2].length() - 1) == 'x' && value[1] - .charAt(value[1].length() - 1) == 'y') - || (value[2].charAt(value[2].length() - 1) == 'y' && value[1] - .charAt(value[1].length() - 1) == 'x')) { - value = new String[] { value[0] }; - } - } - - } - } - - // Support for setting arrays in format - // set-array:name=value1,value2,value3,... - else if (name.startsWith("set-array:")) { - int equalsIndex = name.indexOf('='); - if (equalsIndex < 0) - return; - - StringTokenizer commalist = new StringTokenizer(name - .substring(equalsIndex + 1), ","); - name = name.substring(10, equalsIndex); - String[] curVal = (String[]) values.get(name); - ArrayList elems = new ArrayList(); - - // Adds old values if present. - if (curVal != null) { - for (int i = 0; i < curVal.length; i++) - elems.add(curVal[i]); - } - while (commalist.hasMoreTokens()) { - String token = commalist.nextToken(); - if (token != null && token.length() > 0) - elems.add(token); - } - value = new String[elems.size()]; - for (int i = 0; i < value.length; i++) - value[i] = (String) elems.get(i); - - } - - // Support name="array:name" value="val1,val2,val3" notation - // All the empty elements are ignored - else if (name.startsWith("array:")) { - - name = name.substring(6); - StringTokenizer commalist = new StringTokenizer(value[0], ","); - String[] curVal = (String[]) values.get(name); - ArrayList elems = new ArrayList(); - - // Adds old values if present. - if (curVal != null) { - for (int i = 0; i < curVal.length; i++) - elems.add(curVal[i]); - } - while (commalist.hasMoreTokens()) { - String token = commalist.nextToken(); - if (token != null && token.length() > 0) - elems.add(token); - } - value = new String[elems.size()]; - for (int i = 0; i < value.length; i++) - value[i] = (String) elems.get(i); - } - - // Support declaring variables with name="declare:name" - else if (name.startsWith("declare:")) { - name = name.substring(8); - value = (String[]) values.get(name); - if (value == null) - value = new String[0]; - } - - // Gets the owner - WeakReference ref = (WeakReference) idToOwnerMap.get(name); - VariableOwner owner = null; - if (ref != null) - owner = (VariableOwner) ref.get(); - - // Adds the parameter to mapping only if they have owners - if (owner != null) { - Set p = (Set) parameters.get(owner); - if (p == null) - parameters.put(owner, p = new HashSet()); - p.add(name); - if (value != null) - values.put(name, value); - } - - // If the owner can not be found - else { - - // If parameter has been mapped before, remove the old owner - // mapping - if (ref != null) { - - // The owner has been destroyed, so we remove the mappings - idToNameMap.remove(name); - idToOwnerMap.remove(name); - idToTypeMap.remove(name); - idToValueMap.remove(name); - } - - // Adds the parameter to set of non-variables - nonVariables.put(name, value); - } - - } - - /** - * Gets the set of all parameters connected to given variable owner. - * - * @param owner - * the Listener for variable changes. - * @return the set of all parameters connected to variable owner. - */ - public Set getParameters(VariableOwner owner) { - if (owner == null) - return null; - return (Set) parameters.get(owner); - } - - /** - * Gets the set of all variable owners owning parameters in this - * request. - * - * @return - */ - public Set getOwners() { - return parameters.keySet(); - } - - /** - * Gets the value of a parameter. - * - * @param parameterName - * the name of the parameter. - * @return the value of the parameter. - */ - public String[] getValue(String parameterName) { - return (String[]) values.get(parameterName); - } - - /** - * Gets the servlet multipart parser. - * - * @return the parser. - */ - public ServletMultipartRequest getParser() { - return parser; - } - - /** - * Gets the name - value[] mapping of non variable paramteres. - * - * @return - */ - public Map getNonVariables() { - return nonVariables; - } - } - - /** - * Handles all variable changes in this request. - * - * @param req - * the Http request to handle. - * @param errorListener - * If the list is non null, only the listed listeners are served. - * Otherwise all the listeners are served. - * @return Name to Value[] mapping of unhandled variables. - * @throws IOException - * if the writing failed due to input/output error. - */ - public Map handleVariables(HttpServletRequest req, - Terminal.ErrorListener errorListener) throws IOException { - - // Gets the parameters - ParameterContainer parcon = new ParameterContainer(req); - - // Sorts listeners to dependency order - List listeners = getDependencySortedListenerList(parcon.getOwners()); - - // Handles all parameters for all listeners - while (!listeners.isEmpty()) { - VariableOwner listener = (VariableOwner) listeners.remove(0); - boolean changed = false; // Has any of this owners variabes - // changed - // Handle all parameters for listener - Set params = parcon.getParameters(listener); - if (params != null) { // Name value mapping - Map variables = new HashMap(); - for (Iterator pi = params.iterator(); pi.hasNext();) { - // Gets the name of the parameter - String param = (String) pi.next(); - // Extracts more information about the parameter - String varName = (String) idToNameMap.get(param); - Class varType = (Class) idToTypeMap.get(param); - Object varOldValue = idToValueMap.get(param); - if (varName == null || varType == null) - Log - .warn("VariableMap: No variable found for parameter " - + param - + " (" - + varName - + "," - + listener + ")"); - else { - - ServletMultipartRequest parser = parcon.getParser(); - - // Upload events - if (varType.equals(UploadStream.class)) { - if (parser != null - && parser.getFileParameter(param, - MultipartRequest.FILENAME) != null) { - String filename = (String) parser - .getFileParameter(param, - MultipartRequest.FILENAME); - String contentType = (String) parser - .getFileParameter(param, - MultipartRequest.CONTENT_TYPE); - UploadStream upload = new HttpUploadStream( - varName, parser.getFileContents(param), - filename, contentType); - variables.put(varName, upload); - changed = true; - } - } - - // Normal variable change events - else { - // First try to parse the event without multipart - String[] values = parcon.getValue(param); - if (values != null) { - - if (varType.equals(String[].class)) { - variables.put(varName, values); - changed |= (!Arrays.equals(values, - (String[]) varOldValue)); - } else { - try { - if (values.length == 1) { - Object val = convert(varType, - values[0]); - variables.put(varName, val); - changed |= ((val == null && varOldValue != null) || (val != null && !val - .equals(varOldValue))); - } else if (values.length == 0 - && varType - .equals(Boolean.class)) { - Object val = new Boolean(false); - variables.put(varName, val); - changed |= (!val - .equals(varOldValue)); - } else { - Log.warn("Empty variable '" - + varName + "' of type " - + varType.toString()); - } - - } catch (java.lang.ClassCastException e) { - Log - .except( - "WebVariableMap conversion exception", - e); - errorListener - .terminalError(new TerminalErrorImpl( - e)); - } - } - } - } - } - } - - // Do the valuechange if the listener is enabled - if (listener.isEnabled() && changed) { - try { - listener.changeVariables(req, variables); - } catch (Throwable t) { - // Notify the error listener - errorListener.terminalError(new VariableOwnerErrorImpl( - listener, t)); - } - } - } - } - - return parcon.getNonVariables(); - } - - /** - * Implementation of VariableOwner.Error interface. - */ - public class TerminalErrorImpl implements Terminal.ErrorEvent { - private Throwable throwable; - - /** - * - * @param throwable - */ - private TerminalErrorImpl(Throwable throwable) { - this.throwable = throwable; - } - - /** - * Gets the contained throwable. - * - * @see com.itmill.toolkit.terminal.Terminal.ErrorEvent#getThrowable() - */ - public Throwable getThrowable() { - return this.throwable; - } - - } - - /** - * Implementation of VariableOwner.Error interface. - */ - public class VariableOwnerErrorImpl extends TerminalErrorImpl implements - VariableOwner.ErrorEvent { - - private VariableOwner owner; - - /** - * - * @param owner - * the Listener for variable changes. - * @param throwable - */ - private VariableOwnerErrorImpl(VariableOwner owner, Throwable throwable) { - super(throwable); - this.owner = owner; - } - - /** - * Gets the source VariableOwner. - * - * @see com.itmill.toolkit.terminal.VariableOwner.ErrorEvent#getVariableOwner() - */ - public VariableOwner getVariableOwner() { - return this.owner; - } - - } - - /** - * Resolves the VariableOwners needed from the request and sort them to - * assure that the dependencies are met (as well as possible). - * - * @param listeners - * @return List of variable list changers, that are needed for handling all - * the variables in the request - */ - private List getDependencySortedListenerList(Set listeners) { - - LinkedList resultNormal = new LinkedList(); - LinkedList resultImmediate = new LinkedList(); - - // Go trough the listeners and either add them to result or resolve - // their dependencies - HashMap deepdeps = new HashMap(); - LinkedList unresolved = new LinkedList(); - for (Iterator li = listeners.iterator(); li.hasNext();) { - - VariableOwner listener = (VariableOwner) li.next(); - if (listener != null) { - Set dependencies = listener.getDirectDependencies(); - - // The listeners with no dependencies are added to the front of - // the - // list directly - if (dependencies == null || dependencies.isEmpty()) { - if (listener.isImmediate()) - resultImmediate.addFirst(listener); - else - resultNormal.addFirst(listener); - } - - // Resolve deep dependencies for the listeners with dependencies - // (the listeners will be added to the end of results in correct - // dependency order later). Also the dependencies of all the - // depended listeners are resolved. - else if (deepdeps.get(listener) == null) { - - // Set the fifo for unresolved parents to contain only the - // listener to be resolved - unresolved.clear(); - unresolved.add(listener); - - // Resolve dependencies - HashSet tmpdeepdeps = new HashSet(); - while (!unresolved.isEmpty()) { - - VariableOwner l = (VariableOwner) unresolved - .removeFirst(); - if (!tmpdeepdeps.contains(l)) { - tmpdeepdeps.add(l); - if (deepdeps.containsKey(l)) { - tmpdeepdeps.addAll((Set) deepdeps.get(l)); - } else { - Set deps = l.getDirectDependencies(); - if (deps != null && !deps.isEmpty()) - for (Iterator di = deps.iterator(); di - .hasNext();) { - Object d = di.next(); - if (d != null - && !tmpdeepdeps.contains(d)) - unresolved.addLast(d); - } - } - } - } - - tmpdeepdeps.remove(listener); - deepdeps.put(listener, tmpdeepdeps); - } - } - } - - // Adds the listeners with dependencies in sane order to the result - for (Iterator li = deepdeps.keySet().iterator(); li.hasNext();) { - VariableOwner l = (VariableOwner) li.next(); - boolean immediate = l.isImmediate(); - - // Adds each listener after the last depended listener already in - // the list - int index = -1; - for (Iterator di = ((Set) deepdeps.get(l)).iterator(); di.hasNext();) { - int k; - Object depended = di.next(); - if (immediate) { - k = resultImmediate.lastIndexOf(depended); - } else { - k = resultNormal.lastIndexOf(depended); - } - if (k > index) - index = k; - } - if (immediate) { - resultImmediate.add(index + 1, l); - } else { - resultNormal.add(index + 1, l); - } - } - - // Appends immediate listeners to normal listeners - // This way the normal handlers are always called before - // immediate ones - resultNormal.addAll(resultImmediate); - return resultNormal; - } -} diff --git a/src/com/itmill/toolkit/terminal/web/JarThemeSource.java b/src/com/itmill/toolkit/terminal/web/JarThemeSource.java deleted file mode 100644 index 2761f795ff..0000000000 --- a/src/com/itmill/toolkit/terminal/web/JarThemeSource.java +++ /dev/null @@ -1,406 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.lang.ref.SoftReference; -import java.util.Collection; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.Map; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -/** - * Theme source for reading themes from a JAR archive. At this time only jar - * files are supported and an archive may not contain any recursive archives. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ -public class JarThemeSource implements ThemeSource { - - private File file; - - private JarFile jar; - - private Theme theme; - - private String path; - - private String name; - - private ApplicationServlet webAdapterServlet; - - private Cache resourceCache = new Cache(); - - /** - * Collection of subdirectory entries. - */ - private Collection subdirs = new LinkedList(); - - /** - * Creates a new instance of ThemeRepository by reading the themes from a - * local directory. - * - * @param file - * the Path to the JAR archive . - * @param webAdapterServlet - * @param path - * the Path inside the archive to be processed. - * @throws ThemeException - * If the resource is not found or there was some problem - * finding the resource. - * - * @throws FileNotFoundException - * if no theme files are found. - * @throws IOException - * if the writing failed due to input/output error. - */ - public JarThemeSource(File file, ApplicationServlet webAdapterServlet, - String path) throws ThemeException, FileNotFoundException, - IOException { - - this.file = file; - this.jar = new JarFile(file); - this.theme = null; - this.path = path; - if (this.path.length() > 0 && !this.path.endsWith("/")) { - this.path = this.path + "/"; - } - this.name = file.getName(); - if (this.name.toLowerCase().endsWith(".jar")) { - this.name = this.name.substring(0, this.name.length() - 4); - } - - this.webAdapterServlet = webAdapterServlet; - - // Loads description file - JarEntry entry = jar.getJarEntry(this.path + Theme.DESCRIPTIONFILE); - if (entry != null) { - try { - this.theme = new Theme(jar.getInputStream(entry)); - } catch (Exception e) { - throw new ThemeException("JarThemeSource: Failed to load '" - + path + "': ", e); - } - - // Debug info - if (webAdapterServlet.isDebugMode(null)) { - Log.debug("Added JarThemeSource: " + this.file + ":" - + this.path); - } - - } else { - // There was no description file found. - // Handle subdirectories recursively - for (Enumeration entries = jar.entries(); entries.hasMoreElements();) { - JarEntry e = (JarEntry) entries.nextElement(); - if (e.getName().startsWith(this.path)) { - if (e.getName().endsWith("/") - && e.getName().indexOf('/', this.path.length()) == (e - .getName().length() - 1)) { - this.subdirs.add(new JarThemeSource(this.file, - this.webAdapterServlet, e.getName())); - } - } - } - - if (this.subdirs.isEmpty()) { - if (webAdapterServlet.isDebugMode(null)) { - Log.info("JarThemeSource: Ignoring empty JAR path: " - + this.file + " path: " + this.path); - } - } - } - } - - /** - * Gets the XSL stream for the specified theme and web-browser type. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getXSLStreams(Theme, - * WebBrowser) - */ - public Collection getXSLStreams(Theme theme, WebBrowser type) - throws ThemeException { - Collection xslFiles = new LinkedList(); - // If this directory contains a theme - // return XSL from this theme - if (this.theme != null) { - - if (webAdapterServlet.isDebugMode(null)) { - Log.info("JarThemeSource: Loading XSL from: " + theme); - } - - // Reload the theme if JAR has been modified - JarEntry entry = jar.getJarEntry(this.path + Theme.DESCRIPTIONFILE); - if (entry != null) { - try { - this.theme = new Theme(jar.getInputStream(entry)); - } catch (IOException e) { - throw new ThemeException("Failed to read description: " - + this.file + ":" + this.path - + Theme.DESCRIPTIONFILE); - } - } - - Collection fileNames = theme.getFileNames(type, Theme.MODE_HTML); - // Add all XSL file streams - for (Iterator i = fileNames.iterator(); i.hasNext();) { - entry = jar.getJarEntry(this.path + (String) i.next()); - if (entry.getName().endsWith(".xsl")) - try { - xslFiles.add(new XSLStream(entry.getName(), jar - .getInputStream(entry))); - } catch (java.io.FileNotFoundException e) { - throw new ThemeException("XSL File not found: " - + this.file + ": " + entry); - } catch (java.io.IOException e) { - throw new ThemeException("Failed to read XSL file. " - + this.file + ": " + entry); - } - } - - } else { - - // Handle subdirectories in archive: return the first match - for (Iterator i = this.subdirs.iterator(); i.hasNext();) { - ThemeSource source = (ThemeSource) i.next(); - if (source.getThemes().contains(theme)) - xslFiles.addAll(source.getXSLStreams(theme, type)); - } - } - - return xslFiles; - } - - /** - * Returns modication time of the jar file. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getModificationTime() - */ - public long getModificationTime() { - return this.file.lastModified(); - } - - /** - * Gets the input stream for the resource with the specified resource id. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getResource(String) - */ - public InputStream getResource(String resourceId) - throws ThemeSource.ThemeException { - - // Strip off the theme name prefix from resource id - if (this.theme != null && this.theme.getName() != null - && resourceId.startsWith(this.theme.getName() + "/")) { - resourceId = resourceId - .substring(this.theme.getName().length() + 1); - } - - // Returns the resource inside the jar file - JarEntry entry = jar.getJarEntry(resourceId); - if (entry != null) - try { - - // Try cache - byte[] data = (byte[]) resourceCache.get(entry); - if (data != null) - return new ByteArrayInputStream(data); - - // Reads data - int bufSize = 1024; - ByteArrayOutputStream out = new ByteArrayOutputStream(bufSize); - InputStream in = jar.getInputStream(entry); - byte[] buf = new byte[bufSize]; - int n = 0; - while ((n = in.read(buf)) >= 0) { - out.write(buf, 0, n); - } - in.close(); - data = out.toByteArray(); - - // Cache data - resourceCache.put(entry, data); - return new ByteArrayInputStream(data); - } catch (IOException e) { - } - - throw new ThemeSource.ThemeException("Resource " + resourceId - + " not found."); - } - - /** - * Gets the list of themes in the theme source. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getThemes() - */ - public Collection getThemes() { - Collection themes = new LinkedList(); - if (this.theme != null) { - themes.add(this.theme); - } else { - for (Iterator i = this.subdirs.iterator(); i.hasNext();) { - ThemeSource source = (ThemeSource) i.next(); - themes.addAll(source.getThemes()); - } - } - return themes; - } - - /** - * Gets the name of the ThemeSource. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getName() - */ - public String getName() { - if (this.theme != null) { - return this.theme.getName(); - } else { - return this.name; - } - } - - /** - * Gets the Theme instance by name. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getThemeByName(String) - */ - public Theme getThemeByName(String name) { - Collection themes = this.getThemes(); - for (Iterator i = themes.iterator(); i.hasNext();) { - Theme t = (Theme) i.next(); - if (name != null && name.equals(t.getName())) - return t; - } - return null; - } - - /** - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - private class Cache { - - private Map data = new HashMap(); - - /** - * Associates the specified value with the specified key in this map. If - * the map previously contained a mapping for this key, the old value is - * replaced by the specified value. - * - * @param key - * the key with which the specified value is to be - * associated. - * @param value - * the value to be associated with the specified key. - */ - public void put(Object key, Object value) { - data.put(key, new SoftReference(new CacheItem(value))); - } - - /** - * Returns the value to which this map maps the specified key. Returns - * null if the map contains no mapping for this key. - *

- * A return value of null does not necessarily indicate that the map - * contains no mapping for the key; it's also possible that the map - * explicitly maps the key to null. The containsKey operation may be - * used to distinguish these two cases. - *

- * - * @param key - * the key whose associated value is to be returned. - * @return the value to which this map maps the specified key, or null - * if the map contains no mapping for this key. - */ - public Object get(Object key) { - SoftReference ref = (SoftReference) data.get(key); - if (ref != null) - return ((CacheItem) ref.get()).getData(); - return null; - } - - /** - * Clears the data. - * - */ - public void clear() { - data.clear(); - } - } - - /** - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - private class CacheItem { - - private Object data; - - /** - * - * @param data - */ - public CacheItem(Object data) { - this.data = data; - } - - /** - * - * @return - */ - public Object getData() { - return this.data; - }; - - /** - * @see java.lang.Object#finalize() - */ - public void finalize() throws Throwable { - this.data = null; - super.finalize(); - } - - } - -} diff --git a/src/com/itmill/toolkit/terminal/web/Log.java b/src/com/itmill/toolkit/terminal/web/Log.java deleted file mode 100644 index ddc6ec2e3a..0000000000 --- a/src/com/itmill/toolkit/terminal/web/Log.java +++ /dev/null @@ -1,133 +0,0 @@ -/* ************************************************************************* - - 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.web; - -/** - *

- * Class providing centralized logging services. The logger defines five message - * types, and provides methods to create messages of those types. These types - * are: - *

- * - *
    - *
  • info - Useful information generated during normal - * operation of the application. - *
  • warning - An error situation has occurred, but the - * operation was able to finish succesfully. - *
  • error - An error situation which prevented the operation - * from finishing succesfully. - *
  • debug - Internal information from the application meant - * for developers. - *
  • exception - A Java exception reported using the logger. - * Includes the exception stack trace and a possible free-form message. - *
- * - *

- * Currently the class offers logging only to the standard output. - *

- * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ -public class Log { - - private static boolean useStdOut = true; - - private static String LOG_MSG_INFO = "[INFO]"; - - private static String LOG_MSG_ERROR = "[ERROR]"; - - private static String LOG_MSG_WARN = "[WARNING]"; - - private static String LOG_MSG_DEBUG = "[DEBUG]"; - - private static String LOG_MSG_EXCEPT = "[EXCEPTION]"; - - /** - * Logs the warning message. - * - * @param message - * the Message String to be logged. - */ - protected static synchronized void warn(java.lang.String message) { - if (Log.useStdOut) - System.out.println(LOG_MSG_WARN + " " + message); - } - - /** - * Logs the debug message. - * - * @param message - * the Message String to be logged. - */ - protected static synchronized void debug(java.lang.String message) { - if (Log.useStdOut) - System.out.println(LOG_MSG_DEBUG + " " + message); - } - - /** - * Logs an info message. - * - * @param message - * the Message String to be logged. - */ - protected static synchronized void info(java.lang.String message) { - if (Log.useStdOut) - System.out.println(LOG_MSG_INFO + " " + message); - } - - /** - * Logs the Java exception and an accompanying error message. - * - * @param message - * the Message String to be logged. - * @param e - * the Exception to be logged. - */ - protected static synchronized void except(java.lang.String message, - Exception e) { - if (Log.useStdOut) { - System.out.println(LOG_MSG_EXCEPT + " " + message); - e.printStackTrace(); - } - } - - /** - * Logs the error message. - * - * @param message - * the Message String to be logged. - */ - protected static synchronized void error(java.lang.String message) { - if (Log.useStdOut) - System.out.println(LOG_MSG_ERROR + " " + message); - } -} diff --git a/src/com/itmill/toolkit/terminal/web/MultipartRequest.java b/src/com/itmill/toolkit/terminal/web/MultipartRequest.java deleted file mode 100644 index 3b2620c6a5..0000000000 --- a/src/com/itmill/toolkit/terminal/web/MultipartRequest.java +++ /dev/null @@ -1,1332 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import java.util.Hashtable; -import java.io.BufferedOutputStream; -import java.io.BufferedInputStream; -import java.io.OutputStream; -import java.io.InputStream; -import java.io.PrintWriter; -import java.io.ByteArrayOutputStream; -import java.io.ByteArrayInputStream; -import java.io.FileOutputStream; -import java.io.UnsupportedEncodingException; -import java.io.IOException; -import java.util.Enumeration; -import java.util.Vector; -import java.io.File; - -/** - * A Multipart form data parser. Parses an input stream and writes out any files - * found, making available a hashtable of other url parameters. As of version - * 1.17 the files can be saved to memory, and optionally written to a database, - * etc. - * - *
- *
- * Copyright (C)2001 Jason Pell.
- * - *
- * 
- * 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.1 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
- * Email: jasonpell@hotmail.com Url: http://www.geocities.com/jasonpell - * - *
- * - * @author Jason Pell - * - * @version 1.18 Fixed some serious bugs. A new method readAndWrite(InputStream - * in, OutputStream out) which now does the generic processing in - * common for readAndWriteFile and readFile. The differences are that - * now the two extra bytes at the end of a file upload are processed - * once, instead of after each line. Also if an empty file is - * encountered, an outputstream is opened, but then deleted if no data - * written to it. The getCharArray method has been - * removed. Replaced by the new String(bytes, encoding) method using a - * specific encoding (Defaults to ISO-8859-1) to ensure that extended - * characters are supported. All strings are processed using this - * encoding. The addition of static methods setEncoding(String) and - * getEncoding method to allow the use of - * MultipartRequest with a specific encoding type. All - * instances of MultipartRequest will utilise the static charEncoding - * variable value, that the setEncoding method can be - * used to set. Started to introduce support for multiple file uploads - * with the same form field name, but not completed for v1.18. - * 26/06/2001 - * - * @version 1.17 A few _very_ minor fixes. Plus a cool new feature added. The - * ability to save files into memory. Thanks to Mark Latham for the - * idea and some of the code. 11/04/2001 - * @version 1.16 Added support for multiple parameter values. Also fixed - * getCharArray(...) method to support parameters with non-english - * ascii values (ascii above 127). Thanks to Stefan Schmidt & Michael - * Elvers for this. (No fix yet for reported problems with Tomcat 3.2 - * or a single extra byte appended to uploads of certain files). By - * 1.17 hopefully will have a resolution for the second problem. - * 14/03/2001 - * @version 1.15 A new parameter added, intMaxReadBytes, to allow arbitrary - * length files. Released under the LGPL (Lesser General Public - * License). 03/02/2001 - * @version 1.14 Fix for IE problem with filename being empty. This is because - * IE includes a default Content-Type even when no file is uploaded. - * 16/02/2001 - * @version 1.13 If an upload directory is not specified, then all file contents - * are sent into oblivion, but the rest of the parsing works as normal. - * @version 1.12 Fix, was allowing zero length files. Will not even create the - * output file until there is something to write. getFile(String) now - * returns null, if a zero length file was specified. 06/11/2000 - * @version 1.11 Fix, in case Content-type is not specified. - * @version 1.1 Removed dependence on Servlets. Now passes in a generic - * InputStream instead. "Borrowed" readLine from Tomcat 3.1 - * ServletInputStream class, so we can remove some of the dependencies - * on ServletInputStream. Fixed bug where a empty INPUT TYPE="FILE" - * value, would cause an exception. - * @version 1.0 Initial Release. - */ - -public class MultipartRequest { - /** - * Defines Character Encoding method here. - */ - private String charEncoding = "UTF-8"; - - // If not null, send debugging out here. - private PrintWriter debug = null; - - private Hashtable htParameters = null; - - private Hashtable htFiles = null; - - private String strBoundary = null; - - // If this Directory spec remains null, writing of files will be disabled... - private File fileOutPutDirectory = null; - - private boolean loadIntoMemory = false; - - private long intContentLength = -1; - - private long intTotalRead = -1; - - /** - * Prevent a denial of service by defining this, will never read more data. - * If Content-Length is specified to be more than this, will throw an - * exception. - * - * This limits the maximum number of bytes to the value of an int, which is - * 2 Gigabytes. - */ - public static final int MAX_READ_BYTES = Integer.MAX_VALUE; - - /** - * Defines the number of bytes to read per readLine call. 128K - */ - public static final int READ_LINE_BLOCK = 1024 * 128; - - /** - * Store a read from the input stream here. Global so we do not keep - * creating new arrays each read. - */ - private byte[] blockOfBytes = null; - - /** - * Type constant for File FILENAME. - */ - public static final int FILENAME = 0; - - /** - * Type constant for the File CONTENT_TYPE. - */ - public static final int CONTENT_TYPE = 1; - - /** - * Type constant for the File SIZE. - */ - public static final int SIZE = 2; - - /** - * Type constant for the File CONTENTS. - * - * Note: Only used for file upload to memory. - */ - public static final int CONTENTS = 3; - - /** - * This method should be called on the MultipartRequest - * itself, not on any instances of MultipartRequest, because - * this sets up the encoding for all instances of multipartrequest. You can - * set the encoding to null, in which case the default encoding will be - * applied. The default encoding if this method is not called has been set - * to ISO-8859-1, which seems to offer the best hope of support for - * international characters, such as german "Umlaut" characters. - * - *

- * Warning: In multithreaded environments it is the responsibility - * of the implementer to make sure that this method is not called while - * another instance is being constructed. When an instance of - * MultipartRequest is constructed, it parses the input data, and uses the - * result of getEncoding method to convert between bytes and - * strings. If setEncoding method is called by another - * thread, while the private parse method is executing, the - * method will utilise this new encoding, which may cause serious problems. - *

- */ - public void setEncoding(String enc) throws UnsupportedEncodingException { - if (enc == null || enc.trim() == "") - charEncoding = System.getProperty("file.encoding"); - else { - // This will test the encoding for validity. - new String(new byte[] { '\n' }, enc); - - charEncoding = enc; - } - } - - /** - * Gets the current encoding method. - * - * @return the encoding method. - */ - public String getEncoding() { - return charEncoding; - } - - /** - * Constructor. - * - * @param strContentTypeText - * the "Content-Type" HTTP header value. - * @param intContentLength - * the "Content-Length" HTTP header value. - * @param in - * the InputStream to read and parse. - * @param strSaveDirectory - * the temporary directory to save the file from where they can - * then be moved to wherever by the calling process. If you - * specify null for this parameter, then any files - * uploaded will be silently ignored. - * - * @exception IllegalArgumentException - * If the strContentTypeText does not contain a Content-Type - * of "multipart/form-data" or the boundary is not found. - * @exception IOException - * If the intContentLength is higher than MAX_READ_BYTES or - * strSaveDirectory is invalid or cannot be written to. - * - * @see #MAX_READ_BYTES - */ - public MultipartRequest(String strContentTypeText, int intContentLength, - InputStream in, String strSaveDirectory) - throws IllegalArgumentException, IOException { - this(null, strContentTypeText, intContentLength, in, strSaveDirectory, - MAX_READ_BYTES); - } - - /** - * Constructor. - * - * @param strContentTypeText - * the "Content-Type" HTTP header value. - * @param intContentLength - * the "Content-Length" HTTP header value. - * @param in - * the InputStream to read and parse. - * @param strSaveDirectory - * the temporary directory to save the file from where they can - * then be moved to wherever by the calling process. If you - * specify null for this parameter, then any files - * uploaded will be silently ignored. - * @param intMaxReadBytes - * Overrides the MAX_BYTES_READ value, to allow arbitrarily long - * files. - * - * @exception IllegalArgumentException - * If the strContentTypeText does not contain a Content-Type - * of "multipart/form-data" or the boundary is not found. - * @exception IOException - * If the intContentLength is higher than MAX_READ_BYTES or - * strSaveDirectory is invalid or cannot be written to. - * - * @see #MAX_READ_BYTES - */ - public MultipartRequest(String strContentTypeText, int intContentLength, - InputStream in, String strSaveDirectory, int intMaxReadBytes) - throws IllegalArgumentException, IOException { - this(null, strContentTypeText, intContentLength, in, strSaveDirectory, - intMaxReadBytes); - } - - /** - * Constructor. - * - * @param debug - * A PrintWriter that can be used for debugging. - * @param strContentTypeText - * the "Content-Type" HTTP header value. - * @param intContentLength - * the "Content-Length" HTTP header value. - * @param in - * the InputStream to read and parse. - * @param strSaveDirectory - * the temporary directory to save the file from where they can - * then be moved to wherever by the calling process. If you - * specify null for this parameter, then any files - * uploaded will be silently ignored. - * - * @exception IllegalArgumentException - * If the strContentTypeText does not contain a Content-Type - * of "multipart/form-data" or the boundary is not found. - * @exception IOException - * If the intContentLength is higher than MAX_READ_BYTES or - * strSaveDirectory is invalid or cannot be written to. - * - * @see #MAX_READ_BYTES - * @deprecated Replaced by MultipartRequest(PrintWriter, String, int, - * InputStream, int) You can specify - * MultipartRequest.MAX_READ_BYTES for the intMaxReadBytes - * parameter - */ - public MultipartRequest(PrintWriter debug, String strContentTypeText, - int intContentLength, InputStream in, String strSaveDirectory) - throws IllegalArgumentException, IOException { - this(debug, strContentTypeText, intContentLength, in, strSaveDirectory, - MAX_READ_BYTES); - - } - - /** - * Constructor - load into memory constructor - * - * @param debug - * A PrintWriter that can be used for debugging. - * @param strContentTypeText - * the "Content-Type" HTTP header value. - * @param intContentLength - * the "Content-Length" HTTP header value. - * @param in - * the InputStream to read and parse. - * @param intMaxReadBytes - * Overrides the MAX_BYTES_READ value, to allow arbitrarily long - * files. - * - * @exception IllegalArgumentException - * If the strContentTypeText does not contain a Content-Type - * of "multipart/form-data" or the boundary is not found. - * @exception IOException - * If the intContentLength is higher than MAX_READ_BYTES or - * strSaveDirectory is invalid or cannot be written to. - * - * @see #MAX_READ_BYTES - */ - public MultipartRequest(PrintWriter debug, String strContentTypeText, - int intContentLength, InputStream in, int intMaxReadBytes) - throws IllegalArgumentException, IOException { - this.loadIntoMemory = true; - - // Now initialise the object, which will actually call the parse method - // to parse multipart stream. - init(debug, strContentTypeText, intContentLength, in, intMaxReadBytes); - } - - /** - * Constructor. - * - * @param debug - * A PrintWriter that can be used for debugging. - * @param strContentTypeText - * the "Content-Type" HTTP header value. - * @param intContentLength - * the "Content-Length" HTTP header value. - * @param in - * the InputStream to read and parse. - * @param strSaveDirectory - * the temporary directory to save the file from where they can - * then be moved to wherever by the calling process. If you - * specify null for this parameter, then any files - * uploaded will be silently ignored. - * @param intMaxReadBytes - * Overrides the MAX_BYTES_READ value, to allow arbitrarily long - * files. - * - * @exception IllegalArgumentException - * If the strContentTypeText does not contain a Content-Type - * of "multipart/form-data" or the boundary is not found. - * @exception IOException - * If the intContentLength is higher than MAX_READ_BYTES or - * strSaveDirectory is invalid or cannot be written to. - * - * @see #MAX_READ_BYTES - */ - public MultipartRequest(PrintWriter debug, String strContentTypeText, - int intContentLength, InputStream in, String strSaveDirectory, - int intMaxReadBytes) throws IllegalArgumentException, IOException { - // IF strSaveDirectory == NULL, then we should ignore any files - // uploaded. - if (strSaveDirectory != null) { - fileOutPutDirectory = new File(strSaveDirectory); - if (!fileOutPutDirectory.exists()) - throw new IOException("Directory [" + strSaveDirectory - + "] is invalid."); - else if (!fileOutPutDirectory.canWrite()) - throw new IOException("Directory [" + strSaveDirectory - + "] is readonly."); - } - - // Now initialise the object, which will actually call the parse method - // to parse multipart stream. - init(debug, strContentTypeText, intContentLength, in, intMaxReadBytes); - } - - /** - * Initialise the parser. - * - * @param debug - * A PrintWriter that can be used for debugging. - * @param strContentTypeText - * the "Content-Type" HTTP header value. - * @param intContentLength - * the "Content-Length" HTTP header value. - * @param in - * the InputStream to read and parse. - * @param strSaveDirectory - * the temporary directory to save the file from where they can - * then be moved to wherever by the calling process. If you - * specify null for this parameter, then any files - * uploaded will be silently ignored. - * @param intMaxReadBytes - * Overrides the MAX_BYTES_READ value, to allow arbitrarily long - * files. - * - * @exception IllegalArgumentException - * If the strContentTypeText does not contain a Content-Type - * of "multipart/form-data" or the boundary is not found. - * @exception IOException - * If the intContentLength is higher than MAX_READ_BYTES or - * strSaveDirectory is invalid or cannot be written to. - * - * @see #MAX_READ_BYTES - */ - private void init(PrintWriter debug, String strContentTypeText, - int intContentLength, InputStream in, int intMaxReadBytes) - throws IllegalArgumentException, IOException { - // saves reference to debug stream for later. - this.debug = debug; - - if (strContentTypeText != null - && strContentTypeText.startsWith("multipart/form-data") - && strContentTypeText.indexOf("boundary=") != -1) - strBoundary = strContentTypeText.substring( - strContentTypeText.indexOf("boundary=") - + "boundary=".length()).trim(); - else { - // - debug("ContentType = " + strContentTypeText); - throw new IllegalArgumentException("Invalid Content Type."); - } - - this.intContentLength = intContentLength; - // FIX: 115 - if (intContentLength > intMaxReadBytes) - throw new IOException("Content Length Error (" + intContentLength - + " > " + intMaxReadBytes + ")"); - - // Instantiate the hashtable... - htParameters = new Hashtable(); - htFiles = new Hashtable(); - blockOfBytes = new byte[READ_LINE_BLOCK]; - - // Now parse the data. - parse(new BufferedInputStream(in)); - - // No need for this once parse is complete. - this.blockOfBytes = null; - this.debug = null; - this.strBoundary = null; - } - - /** - * Gets the value of the strName URLParameter. If more than one value for a - * particular Parameter, will return the first. If an error occurs will - * return null. - * - * @param strName - * the form field name, used to upload the file. This identifies - * the formfield location in the storage facility. - * @return the value of the URL Parameter. - */ - public String getURLParameter(String strName) { - Object value = htParameters.get(strName); - if (value instanceof Vector) - return (String) ((Vector) value).firstElement(); - else - return (String) htParameters.get(strName); - } - - /** - * Gets an enumeration of all values for the strName parameter. Even if a - * single value for, will always return an enumeration, although it may - * actually be empty if no value was encountered for strName or it is an - * invalid parameter name. - * - * @param strName - * the form field name, used to upload the file. This identifies - * the formfield location in the storage facility. - * @return the enumeration of all values. - */ - public Enumeration getURLParameters(String strName) { - Object value = htParameters.get(strName); - if (value instanceof Vector) - return ((Vector) value).elements(); - else { - Vector vector = new Vector(); - if (value != null) - vector.addElement(value); - return vector.elements(); - } - } - - /** - * Gets the enumeration of all URL Parameters for the current HTTP Request. - * - * @return the enumeration of URl Parameters. - */ - public Enumeration getParameterNames() { - return htParameters.keys(); - } - - /** - * Gets the enumeration of all INPUT TYPE=FILE parameter NAMES as - * encountered during the upload. - * - * @return - */ - public Enumeration getFileParameterNames() { - return htFiles.keys(); - } - - /** - * Returns the Content-Type of a file. - * - * @see #getFileParameter(java.lang.String, int) - */ - public String getContentType(String strName) { - // Can cast null, it will be ignored. - return (String) getFileParameter(strName, CONTENT_TYPE); - } - - /** - * If files were uploaded into memory, this method will retrieve the - * contents of the file as a InputStream. - * - * @param strName - * the form field name, used to upload the file. This identifies - * the formfield location in the storage facility. - * @return the contents of the file as a InputStream, or null if not file - * uploaded, or file uploaded to file system directory. - * @see #getFileParameter(java.lang.String, int) - */ - public InputStream getFileContents(String strName) { - Object obj = getFileParameter(strName, CONTENTS); - if (obj != null) - return new ByteArrayInputStream((byte[]) obj); - else - return null; - } - - /** - * Returns a File reference to the uploaded file. This reference is to the - * files uploaded location, and allows you to read/move/delete the file. - * This method is only of use, if files were uploaded to the file system. - * Will return null if uploaded to memory, in which case you should use - * getFileContents(strName) instead. - * - * @param strName - * the form field name, used to upload the file. This identifies - * the formfield location in the storage facility. - * @return a null file reference if a call to getFileSize(strName) returns - * zero or files were uploaded to memory. - * @see #getFileSize(java.lang.String) - * @see #getFileContents(java.lang.String) - * @see #getFileSystemName(java.lang.String) - */ - public File getFile(String strName) { - String filename = getFileSystemName(strName); - // Fix: If fileOutPutDirectory is null, then we are ignoring any file - // contents, so we must return null. - if (filename != null && getFileSize(strName) > 0 - && fileOutPutDirectory != null) - return new File(fileOutPutDirectory, filename); - else - return null; - } - - /** - * Gets the file system basename of an uploaded file. - * - * @param strName - * the form field name, used to upload the file. This identifies - * the formfield location in the storage facility. - * @return null if strName not found. - * - * @see #getFileParameter(java.lang.String, int) - */ - public String getFileSystemName(String strName) { - // Can cast null, it will be ignored. - return (String) getFileParameter(strName, FILENAME); - } - - /** - * Returns the File Size of a uploaded file. - * - * @param strName - * the form field name, used to upload the file. This identifies - * the formfield location in the storage facility. - * @return -1 if file size not defined. - * - * @see #getFileParameter(java.lang.String, int) - */ - public long getFileSize(String strName) { - Object obj = getFileParameter(strName, SIZE); - if (obj != null) - return ((Long) obj).longValue(); - else - return (long) -1; - } - - /** - * Access an attribute of a file upload parameter record. - *

- * The getFileSystemName(String strName),getFileSize(String - * strName),getContentType(String strName), getContents(String strName) - * methods all use this method passing in a different type argument. - *

- * - *

- * Note: This class has been changed to provide for future - * functionality where you will be able to access all files uploaded, even - * if they are uploaded using the same form field name. At this point - * however, only the first file uploaded via a form field name is - * accessible. - *

- * - * @param strName - * the form field name, used to upload the file. This identifies - * the formfield location in the storage facility. - * - * @param type - * What attribute you want from the File Parameter. The following - * types are supported: MultipartRequest.FILENAME, - * MultipartRequest.CONTENT_TYPE, MultipartRequest.SIZE, - * MultipartRequest.CONTENTS - * - * @see #getContentType(java.lang.String) - * @see #getFileSize(java.lang.String) - * @see #getFileContents(java.lang.String) - * @see #getFileSystemName(java.lang.String) - */ - public Object getFileParameter(String strName, int type) { - Object[] objArray = null; - Object value = htFiles.get(strName); - if (value instanceof Vector) - objArray = (Object[]) ((Vector) value).firstElement(); - else - objArray = (Object[]) htFiles.get(strName); - - // Now ensure valid value. - if (objArray != null && type >= FILENAME && type <= CONTENTS) - return objArray[type]; - else - return null; - } - - /** - * This is the main parse method. - * - * @param in - * the InputStream to read and parse. - * @throws IOException - * If the intContentLength is higher than MAX_READ_BYTES or - * strSaveDirectory is invalid or cannot be written to. - */ - private void parse(InputStream in) throws IOException { - String strContentType = null; - String strName = null; - String strFilename = null; - String strLine = null; - int read = -1; - - // First run through, check that the first line is a boundary, otherwise - // throw a exception as format incorrect. - read = readLine(in, blockOfBytes); - strLine = read > 0 ? new String(blockOfBytes, 0, read, charEncoding) - : null; - - // Must be boundary at top of loop, otherwise we have finished. - if (strLine == null || strLine.indexOf(this.strBoundary) == -1) - // Just exit. Exception would be overkill - return; - // throw new IOException("Invalid Form Data, no boundary encountered."); - - // At the top of loop, we assume that the Content-Disposition line is - // next, otherwise we are at the end. - while (true) { - // Get Content-Disposition line. - read = readLine(in, blockOfBytes); - if (read <= 0) - break; // Nothing to do. - else { - strLine = new String(blockOfBytes, 0, read, charEncoding); - - // Mac IE4 adds extra line after last boundary - 1.21 - if (strLine == null || strLine.length() == 0 - || strLine.trim().length() == 0) - break; - - strName = trimQuotes(getValue("name", strLine)); - // If this is not null, it indicates that we are processing a - // filename. - strFilename = trimQuotes(getValue("filename", strLine)); - // Now if not null, strip it of any directory information. - - if (strFilename != null) { - // Fix: did not check whether filename was empty string - // indicating FILE contents were not passed. - if (strFilename.length() > 0) { - // Need to get the content type. - read = readLine(in, blockOfBytes); - strLine = read > 0 ? new String(blockOfBytes, 0, read, - charEncoding) : null; - - strContentType = "application/octet-stream"; - // Fix 1.11: If not null AND strLine.length() is long - // enough. - if (strLine != null - && strLine.length() > "Content-Type: ".length()) - strContentType = strLine.substring("Content-Type: " - .length());// Changed 1.13 - } else { - // FIX 1.14: IE problem with empty filename. - read = readLine(in, blockOfBytes); - strLine = read > 0 ? new String(blockOfBytes, 0, read, - charEncoding) : null; - - if (strLine != null - && strLine.startsWith("Content-Type:")) - readLine(in, blockOfBytes); - } - } - - // Ignore next line, as it should be blank. - readLine(in, blockOfBytes); - - // No filename specified at all. - if (strFilename == null) { - String param = readParameter(in); - addParameter(strName, param); - } else { - if (strFilename.length() > 0) { - long filesize = -1; - // Will remain null for read onto file system uploads. - byte[] contentsOfFile = null; - - // Get the BASENAME version of strFilename. - strFilename = getBasename(strFilename); - - // Are we loading files into memory instead of the - // filesystem? - if (loadIntoMemory) { - contentsOfFile = readFile(in); - if (contentsOfFile != null) - filesize = contentsOfFile.length; - } else - // Read the file onto file system. - filesize = readAndWriteFile(in, strFilename); - - // Fix 1.18 for multiple FILE parameter values. - if (filesize > 0) - addFileParameter(strName, new Object[] { - strFilename, strContentType, - new Long(filesize), contentsOfFile }); - else - // Zero length file. - addFileParameter(strName, new Object[] { - strFilename, null, new Long(0), null }); - } else // Fix: FILE INPUT TYPE, but no file passed as - // input... - { - addFileParameter(strName, new Object[] { null, null, - null, null }); - readLine(in, blockOfBytes); - } - } - } - }// while - } - - /** - * So we can put the logic for supporting multiple parameters with the same - * form field name in the one location. - * - * @param strName - * the form field name, used to upload the file. This identifies - * the formfield location in the storage facility. - * @param value - * the form field value. - */ - private void addParameter(String strName, String value) { - // Fix NPE in case of null name - if (strName == null) - return; - - // Fix 1.16: for multiple parameter values. - Object objParms = htParameters.get(strName); - - // Adds an new entry to the param vector. - if (objParms instanceof Vector) - ((Vector) objParms).addElement(value); - else if (objParms instanceof String)// There is only one entry, so we - // create a vector! - { - Vector vecParms = new Vector(); - vecParms.addElement(objParms); - vecParms.addElement(value); - - htParameters.put(strName, vecParms); - } else - // first entry for strName. - htParameters.put(strName, value); - } - - /** - * So we can put the logic for supporting multiple files with the same form - * field name in the one location. - * - * Assumes that this method will never be called with a null fileObj or - * strFilename. - * - * @param strName - * the form field name, used to upload the file. This identifies - * the formfield location in the storage facility. - * @param fileObj - */ - private void addFileParameter(String strName, Object[] fileObj) { - Object objParms = htFiles.get(strName); - - // Add an new entry to the param vector. - if (objParms instanceof Vector) - ((Vector) objParms).addElement(fileObj); - else if (objParms instanceof Object[])// There is only one entry, so - // we create a vector! - { - Vector vecParms = new Vector(); - vecParms.addElement(objParms); - vecParms.addElement(fileObj); - - htFiles.put(strName, vecParms); - } else - // first entry for strName. - htFiles.put(strName, fileObj); - } - - /** - * Reads the parameters, assume already passed Content-Disposition and blank - * line. - * - * @param in - * the InputStream to read and parse. - * @return the value read in. - * @throws IOException - * if an error occurs writing the file. - */ - private String readParameter(InputStream in) throws IOException { - StringBuffer buf = new StringBuffer(); - int read = -1; - - String line = null; - while (true) { - read = readLine(in, blockOfBytes); - if (read < 0) - throw new IOException("Stream ended prematurely."); - - // Change v1.18: Only instantiate string once for performance - // reasons. - line = new String(blockOfBytes, 0, read, charEncoding); - if (read < blockOfBytes.length - && line.indexOf(this.strBoundary) != -1) - break; // Boundary found, we need to finish up. - else - buf.append(line); - } - - if (buf.length() > 0) - buf.setLength(getLengthMinusEnding(buf)); - return buf.toString(); - } - - /** - * Read from in, write to out, minus last two line ending bytes. - * - * @param in - * the InputStream to read and parse. - * @param out - * the OutputStream. - * @throws IOException - * if an error occurs writing the file. - */ - private long readAndWrite(InputStream in, OutputStream out) - throws IOException { - long fileSize = 0; - int read = -1; - - // This variable will be assigned the bytes actually read. - byte[] secondLineOfBytes = new byte[blockOfBytes.length]; - // So we do not have to keep creating the second array. - int sizeOfSecondArray = 0; - - while (true) { - read = readLine(in, blockOfBytes); - if (read < 0) - throw new IOException("Stream ended prematurely."); - - // Found boundary. - if (read < blockOfBytes.length - && new String(blockOfBytes, 0, read, charEncoding) - .indexOf(this.strBoundary) != -1) { - // Writes the line, minus any line ending bytes. - // The secondLineOfBytes will NEVER BE NON-NULL if out==null, so - // there is no need to included this in the test - if (sizeOfSecondArray != 0) { - // Only used once, so declare here. - int actualLength = getLengthMinusEnding(secondLineOfBytes, - sizeOfSecondArray); - if (actualLength > 0 && out != null) { - out.write(secondLineOfBytes, 0, actualLength); - // Updates the file size. - fileSize += actualLength; - } - } - break; - } else { - // Writes the out previous line. - // The sizeOfSecondArray will NEVER BE ZERO if out==null, so - // there is no need to included this in the test - if (sizeOfSecondArray != 0) { - out.write(secondLineOfBytes, 0, sizeOfSecondArray); - // Updates the file size. - fileSize += sizeOfSecondArray; - } - - // out will always be null, so there is no need to reset - // sizeOfSecondArray to zero each time. - if (out != null) { - // Copy the read bytes into the array. - System.arraycopy(blockOfBytes, 0, secondLineOfBytes, 0, - read); - // That is how many bytes to read from the secondLineOfBytes - sizeOfSecondArray = read; - } - } - } - - // Returns the number of bytes written to outstream. - return fileSize; - } - - /** - * Reads a Multipart section that is a file type. Assumes that the - * Content-Disposition/Content-Type and blank line have already been - * processed. So we read until we hit a boundary, then close file and - * return. - * - * @param in - * the InputStream to read and parse. - * @param strFilename - * the FileSystemName of the file. - * @return the number of bytes read. - * @throws IOException - * if an error occurs writing the file. - */ - private long readAndWriteFile(InputStream in, String strFilename) - throws IOException { - // Stores a reference to this, as we may need to delete it later. - File outFile = new File(fileOutPutDirectory, strFilename); - - BufferedOutputStream out = null; - // Do not bother opening a OutputStream, if we cannot even write the - // file. - if (fileOutPutDirectory != null) - out = new BufferedOutputStream(new FileOutputStream(outFile)); - - long count = readAndWrite(in, out); - // Count would NOT be larger than zero if out was null. - if (count > 0) { - out.flush(); - out.close(); - } else { - out.close(); - // Deletes the file as empty. We should be able to delete it, if we - // can open it! - outFile.delete(); - } - return count; - } - - /** - * If the fileOutPutDirectory wasn't specified, just read the file to - * memory. - * - * @param in - * the InputStream to read and parse. - * @return contents of file, from which you can garner the size as well. - * @throws IOException - * if the writing failed due to input/output error. - */ - private byte[] readFile(InputStream in) throws IOException { - // In this case, we do not need to worry about a outputdirectory. - ByteArrayOutputStream out = new ByteArrayOutputStream(); - - long count = readAndWrite(in, out); - // Count would NOT be larger than zero if out was null. - if (count > 0) { - // Return contents of file to parse method for inclusion in htFiles - // object. - return out.toByteArray(); - } else - return null; - } - - /** - * Gets the length of the line minus line ending. - * - * @param byteLine - * @param endOfArray - * This is because in many cases the byteLine will have garbage - * data at the end, so we act as though the actual end of the - * array is this parameter. If you want to process the complete - * byteLine, specify byteLine.length as the endOfArray parameter. - * @return the length. - */ - private static final int getLengthMinusEnding(byte byteLine[], - int endOfArray) { - if (byteLine == null) - return 0; - - if (endOfArray >= 2 && byteLine[endOfArray - 2] == '\r' - && byteLine[endOfArray - 1] == '\n') - return endOfArray - 2; - else if (endOfArray >= 1 && byteLine[endOfArray - 1] == '\n' - || byteLine[endOfArray - 1] == '\r') - return endOfArray - 1; - else - return endOfArray; - } - - /** - * - * @param buf - * @return - */ - private static final int getLengthMinusEnding(StringBuffer buf) { - if (buf.length() >= 2 && buf.charAt(buf.length() - 2) == '\r' - && buf.charAt(buf.length() - 1) == '\n') - return buf.length() - 2; - else if (buf.length() >= 1 && buf.charAt(buf.length() - 1) == '\n' - || buf.charAt(buf.length() - 1) == '\r') - return buf.length() - 1; - else - return buf.length(); - } - - /** - * Reads at most READ_BLOCK blocks of data, or a single line whichever is - * smaller. Returns -1, if nothing to read, or we have reached the specified - * content-length. - * - * Assumes that bytToBeRead.length indicates the block size to read. - * - * @param in - * the InputStream to read and parse. - * @param bytesToBeRead - * the bytes to be read. - * @return -1 if stream has ended, before a newline encountered (should - * never happen) OR we have read past the Content-Length specified. - * (Should also not happen). Otherwise return the number of - * characters read. You can test whether the number returned is less - * than bytesToBeRead.length, which indicates that we have read the - * last line of a file or parameter or a border line, or some other - * formatting stuff. - * @throws IOException - * if the writing failed due to input/output error. - */ - private int readLine(InputStream in, byte[] bytesToBeRead) - throws IOException { - // Ensure that there is still stuff to read... - if (intTotalRead >= intContentLength) - return -1; - - // Get the length of what we are wanting to read. - int length = bytesToBeRead.length; - - // End of content, but some servers (apparently) may not realise this - // and end the InputStream, so - // we cover ourselves this way. - if (length > (intContentLength - intTotalRead)) - length = (int) (intContentLength - intTotalRead); // So we only - // read the data - // that is left. - - int result = readLine(in, bytesToBeRead, 0, length); - // Only if we get actually read something, otherwise something weird has - // happened, such as the end of stream. - if (result > 0) - intTotalRead += result; - - return result; - } - - /** - * This needs to support the possibility of a / or a \ separator. - * - * @param strFilename - * the FileSystemName of the file. - * @return the strFilename after removing all characters before the last - * occurence of / or \. - */ - private static final String getBasename(String strFilename) { - if (strFilename == null) - return strFilename; - - int intIndex = strFilename.lastIndexOf("/"); - if (intIndex == -1 || strFilename.lastIndexOf("\\") > intIndex) - intIndex = strFilename.lastIndexOf("\\"); - - if (intIndex != -1) - return strFilename.substring(intIndex + 1); - else - return strFilename; - } - - /** - * Trims any quotes from the start and end of a string. - * - * @param strItem - * @return the trimmed string. - */ - private static final String trimQuotes(String strItem) { - // Saves having to go any further.... - if (strItem == null || strItem.indexOf("\"") == -1) - return strItem; - - // Gets the rid of any whitespace.. - strItem = strItem.trim(); - - if (strItem.charAt(0) == '\"') - strItem = strItem.substring(1); - - if (strItem.charAt(strItem.length() - 1) == '\"') - strItem = strItem.substring(0, strItem.length() - 1); - - return strItem; - } - - /** - * Format of string name=value; name=value; If not found, will return null. - * - * @param strName - * the form field name, used to upload the file. This identifies - * the formfield location in the storage facility. - * @param strToDecode - * - */ - private static final String getValue(String strName, String strToDecode) { - strName = strName + "="; - - int startIndexOf = 0; - while (startIndexOf < strToDecode.length()) { - int indexOf = strToDecode.indexOf(strName, startIndexOf); - // Ensure either first name, or a space or ; precedes it. - if (indexOf != -1) { - if (indexOf == 0 - || Character.isWhitespace(strToDecode - .charAt(indexOf - 1)) - || strToDecode.charAt(indexOf - 1) == ';') { - int endIndexOf = strToDecode.indexOf(";", indexOf - + strName.length()); - if (endIndexOf == -1) // May return an empty string... - return strToDecode - .substring(indexOf + strName.length()); - else - return strToDecode.substring( - indexOf + strName.length(), endIndexOf); - } else - startIndexOf = indexOf + strName.length(); - } else - return null; - } - return null; - } - - /** - * Tomcat's ServletInputStream.readLine(byte[],int,int) Slightly Modified - * to utilise in.read()
- * Reads the input stream, one line at a time. Starting at an offset, reads - * bytes into an array, until it reads a certain number of bytes or reaches - * a newline character, which it reads into the array as well. - * - *

- * This method does not returns -1 if it reaches the end of - * the input stream before reading the maximum number of bytes, it returns - * -1, if no bytes read. - * - * @param in - * the InputStream to read and parse. - * @param b - * an array of bytes into which data is read. - * - * @param off - * an integer specifying the character at which this method - * begins reading. - * - * @param len - * an integer specifying the maximum number of bytes to read. - * - * @return an integer specifying the actual number of bytes read, or -1 if - * the end of the stream is reached. - * - * @throws IOException - * if an input or output exception has occurred - * - * - * Note: We have a problem with Tomcat reporting an erroneous number of - * bytes, so we need to check this. This is the method where we get an - * infinite loop, but only with binary files. - */ - private int readLine(InputStream in, byte[] b, int off, int len) - throws IOException { - if (len <= 0) - return 0; - - int count = 0, c; - - while ((c = in.read()) != -1) { - b[off++] = (byte) c; - count++; - if (c == '\n' || count == len) - break; - } - - return count > 0 ? count : -1; - } - - /** - * Prints the given debugging message. - * - * @param x - * the message to print. - */ - protected void debug(String x) { - if (debug != null) { - debug.println(x); - debug.flush(); - } - } - - /** - * Gets the Html Table.For debugging. - */ - public String getHtmlTable() { - StringBuffer sbReturn = new StringBuffer(); - - sbReturn.append("

Parameters

"); - sbReturn - .append("\n"); - for (Enumeration e = getParameterNames(); e.hasMoreElements();) { - String strName = (String) e.nextElement(); - sbReturn.append("\n" + ""); - - sbReturn.append(""); - } - sbReturn.append("
NameValue
" + strName + ""); - for (Enumeration f = getURLParameters(strName); f.hasMoreElements();) { - String value = (String) f.nextElement(); - sbReturn.append(""); - } - sbReturn.append("
" + value + "
"); - - sbReturn.append("

File Parameters

"); - - sbReturn - .append("\n"); - for (Enumeration e = getFileParameterNames(); e.hasMoreElements();) { - String strName = (String) e.nextElement(); - - sbReturn - .append("\n" - + "" - + ""); - - if (loadIntoMemory) - sbReturn.append(""); - else - sbReturn.append(""); - - sbReturn - .append("" - + "" + ""); - } - sbReturn.append("
NameFilenamePathContent TypeSize
" - + strName - + "" - + (getFileSystemName(strName) != null ? getFileSystemName(strName) - : "") + "" - + (getFileSize(strName) > 0 ? "in memory" : "") - + "" - + (getFile(strName) != null ? getFile(strName) - .getAbsolutePath() : "") + "" - + (getContentType(strName) != null ? getContentType(strName) - : "") - + "" - + (getFileSize(strName) != -1 ? getFileSize(strName) - + "" - : "") + "
"); - - return sbReturn.toString(); - } -} diff --git a/src/com/itmill/toolkit/terminal/web/ServletMultipartRequest.java b/src/com/itmill/toolkit/terminal/web/ServletMultipartRequest.java deleted file mode 100644 index 1980b0e92c..0000000000 --- a/src/com/itmill/toolkit/terminal/web/ServletMultipartRequest.java +++ /dev/null @@ -1,139 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import java.io.IOException; - -import javax.servlet.http.HttpServletRequest; - -/** - * This class wraps the MultipartRequest class by Jason Pell for the Servlet - * environment. - * - * @author IT Mill Ltd - * @version - * @VERSION@ - * @since 3.0 - */ -public class ServletMultipartRequest extends MultipartRequest { - /** - * Constructor wrapper, unwraps the InputStream, content type and content - * length from the servlet request object. - * - * @param request - * the HttpServletRequest will be used to initialise the - * MultipartRequest super class. - * @param strSaveDirectory - * the temporary directory to save the file from where they can - * then be moved to wherever by the calling process. If you - * specify null for this parameter, then any files - * uploaded will be silently ignored. - * - * @throws IllegalArgumentException - * If the request.getContentType() does not contain a - * Content-Type of "multipart/form-data" or the boundary is not - * found. - * @throws IOException - * If the request.getContentLength() is higher than - * MAX_READ_BYTES or strSaveDirectory is invalid or cannot be - * written to. - * - * @see MultipartRequest#MAX_READ_BYTES - */ - public ServletMultipartRequest(HttpServletRequest request, - String strSaveDirectory) throws IllegalArgumentException, - IOException { - super(null, request.getContentType(), request.getContentLength(), - request.getInputStream(), strSaveDirectory, - MultipartRequest.MAX_READ_BYTES); - } - - /** - * Constructor wrapper, unwraps the InputStream, content type and content - * lenght from the servlet request object. Also allow to explicitly set the - * max permissable length of the request. - * - * @param request - * the HttpServletRequest will be used to initialise the - * MultipartRequest super class. - * @param strSaveDirectory - * the temporary directory to save the file from where they can - * then be moved to wherever by the calling process. If you - * specify null for this parameter, then any files - * uploaded will be silently ignored. - * @param intMaxReadBytes - * Overrides the MAX_BYTES_READ value, to allow arbitrarily long - * files. - * - * @throws IllegalArgumentException - * If the request.getContentType() does not contain a - * Content-Type of "multipart/form-data" or the boundary is not - * found. - * @throws IOException - * If the request.getContentLength() is higher than - * MAX_READ_BYTES or strSaveDirectory is invalid or cannot be - * written to. - * - * @see MultipartRequest#MAX_READ_BYTES - */ - public ServletMultipartRequest(HttpServletRequest request, - String strSaveDirectory, int intMaxReadBytes) - throws IllegalArgumentException, IOException { - super(null, request.getContentType(), request.getContentLength(), - request.getInputStream(), strSaveDirectory, intMaxReadBytes); - } - - /** - * Constructor wrapper for loading the request into memory rather than - * temp-file. - * - * @param request - * the HttpServletRequest will be used to initialise the - * MultipartRequest super class. - * @param intMaxReadBytes - * Overrides the MAX_BYTES_READ value, to allow arbitrarily long - * files. - * - * @throws IllegalArgumentException - * If the request.getContentType() does not contain a - * Content-Type of "multipart/form-data" or the boundary is not - * found. - * @throws IOException - * If the request.getContentLength() is higher than - * MAX_READ_BYTES or strSaveDirectory is invalid or cannot be - * written to. - * - * @see MultipartRequest#MAX_READ_BYTES - */ - public ServletMultipartRequest(HttpServletRequest request, - int intMaxReadBytes) throws IllegalArgumentException, IOException { - super(null, request.getContentType(), request.getContentLength(), - request.getInputStream(), intMaxReadBytes); - } -} diff --git a/src/com/itmill/toolkit/terminal/web/ServletThemeSource.java b/src/com/itmill/toolkit/terminal/web/ServletThemeSource.java deleted file mode 100644 index 8221d04ab7..0000000000 --- a/src/com/itmill/toolkit/terminal/web/ServletThemeSource.java +++ /dev/null @@ -1,392 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.ref.SoftReference; -import java.net.URL; -import java.net.URLConnection; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.Map; - -import javax.servlet.ServletContext; - -/** - * Theme source for reading themes from a JAR archive. At this time only jar - * files are supported and an archive may not contain any recursive archives. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ -public class ServletThemeSource implements ThemeSource { - - private ServletContext context; - - private Theme theme; - - private String path; - - private ApplicationServlet webAdapterServlet; - - private Cache resourceCache = new Cache(); - - /** - * Collection of subdirectory entries. - */ - private URL descFile; - - /** - * Creates a new instance of ThemeRepository by reading the themes from a - * local directory. - * - * @param file - * the Path to the JAR archive . - * @param path - * the Path inside the archive to be processed. - * @throws IOException - * if the writing failed due to input/output error. - * @throws ThemeException - * If the resource is not found or there was some problem - * finding the resource. - */ - public ServletThemeSource(ServletContext context, - ApplicationServlet webAdapterServlet, String path) - throws IOException, ThemeException { - - this.theme = null; - this.webAdapterServlet = webAdapterServlet; - this.context = context; - - // Formats path - this.path = path; - if ((this.path.length() > 0) && !this.path.endsWith("/")) { - this.path = this.path + "/"; - } - if ((this.path.length() > 0) && !this.path.startsWith("/")) { - this.path = "/" + this.path; - } - - // Loads description file - this.descFile = context.getResource(this.path + Theme.DESCRIPTIONFILE); - InputStream entry = context.getResourceAsStream(this.path - + Theme.DESCRIPTIONFILE); - try { - if (entry != null) { - try { - this.theme = new Theme(entry); - } catch (Exception e) { - throw new ThemeException( - "ServletThemeSource: Failed to load '" + path - + "': " + e); - } - entry.close(); - - // Debug info - if (webAdapterServlet.isDebugMode(null)) { - Log.debug("Added ServletThemeSource: " + this.path); - } - - } else { - throw new IllegalArgumentException( - "ServletThemeSource: Invalid theme resource: " + path); - } - } finally { - if (entry != null) - entry.close(); - } - } - - /** - * Gets the XSL stream for the specified theme and web-browser type. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getXSLStreams(Theme, - * WebBrowser) - */ - public Collection getXSLStreams(Theme theme, WebBrowser type) - throws ThemeException { - Collection xslFiles = new LinkedList(); - - // If this directory contains a theme - // return XSL from this theme - if (this.theme != null) { - - if (webAdapterServlet.isDebugMode(null)) { - Log.info("ServletThemeSource: Loading theme: " + theme); - } - - // Reloads the description file - InputStream entry = context.getResourceAsStream(this.path - + Theme.DESCRIPTIONFILE); - try { - if (entry != null) { - this.theme = new Theme(entry); - } - } catch (Exception e) { - throw new ThemeException("ServletThemeSource: Failed to load '" - + path + "': " + e); - } finally { - if (entry != null) - try { - entry.close(); - } catch (IOException ignored) { - } - } - - Collection fileNames = theme.getFileNames(type, Theme.MODE_HTML); - // Adds all XSL file streams - for (Iterator i = fileNames.iterator(); i.hasNext();) { - String entryName = (String) i.next(); - if (entryName.endsWith(".xsl")) { - - entry = context - .getResourceAsStream((this.path + entryName)); - xslFiles.add(new XSLStream(entryName, entry)); - } - } - - } - return xslFiles; - } - - /** - * Returns the modification time of the description file. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getModificationTime() - */ - public long getModificationTime() { - long modTime = 0; - try { - URLConnection conn = this.descFile.openConnection(); - modTime = conn.getLastModified(); - } catch (Exception ignored) { - // In case of exceptions, return zero - } - return modTime; - } - - /** - * Gets the input stream for the resource with the specified resource id. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getResource(String) - */ - public InputStream getResource(String resourceId) - throws ThemeSource.ThemeException { - - // Checks the id - String name = this.getName(); - int namelen = name.length(); - if (resourceId == null || !resourceId.startsWith(name + "/") - || resourceId.length() <= (namelen + 1)) { - return null; - } - - // Finds the resource - String streamName = this.path + resourceId.substring(namelen + 1); - InputStream stream = context.getResourceAsStream(streamName); - if (stream != null) - try { - - // Try cache - byte[] data = (byte[]) resourceCache.get(stream); - if (data != null) - return new ByteArrayInputStream(data); - - // Read data - int bufSize = 1024; - ByteArrayOutputStream out = new ByteArrayOutputStream(bufSize); - byte[] buf = new byte[bufSize]; - int n = 0; - while ((n = stream.read(buf)) >= 0) { - out.write(buf, 0, n); - } - try { - stream.close(); - } catch (IOException ignored) { - } - data = out.toByteArray(); - - // Cache data - resourceCache.put(stream, data); - return new ByteArrayInputStream(data); - } catch (IOException e) { - } - - throw new ThemeSource.ThemeException("Resource " + resourceId - + " not found."); - } - - /** - * Gets the list of themes in the theme source. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getThemes() - */ - public Collection getThemes() { - Collection themes = new LinkedList(); - if (this.theme != null) { - themes.add(this.theme); - } - return themes; - } - - /** - * Gets the name of the ThemeSource. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getName() - */ - public String getName() { - return this.theme.getName(); - } - - /** - * Gets the Theme instance by name. - * - * @see com.itmill.toolkit.terminal.web.ThemeSource#getThemeByName(String) - */ - public Theme getThemeByName(String name) { - Collection themes = this.getThemes(); - for (Iterator i = themes.iterator(); i.hasNext();) { - Theme t = (Theme) i.next(); - if (name != null && name.equals(t.getName())) - return t; - } - return null; - } - - /** - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - private class Cache { - - private Map data = new HashMap(); - - /** - * Associates the specified value with the specified key in this map. If - * the map previously contained a mapping for this key, the old value is - * replaced by the specified value. - * - * @param key - * the key with which the specified value is to be - * associated. - * @param value - * the value to be associated with the specified key. - */ - public void put(Object key, Object value) { - data.put(key, new SoftReference(new CacheItem(value))); - } - - /** - * Returns the value to which this map maps the specified key. Returns - * null if the map contains no mapping for this key. - *

- * A return value of null does not necessarily indicate that the map - * contains no mapping for the key; it's also possible that the map - * explicitly maps the key to null. The containsKey operation may be - * used to distinguish these two cases. - *

- * - * @param key - * the key whose associated value is to be returned. - * @return the value to which this map maps the specified key, or null - * if the map contains no mapping for this key. - */ - public Object get(Object key) { - SoftReference ref = (SoftReference) data.get(key); - if (ref != null) - return ((CacheItem) ref.get()).getData(); - return null; - } - - /** - * Clears the data and removes all mappings from this map . - */ - public void clear() { - data.clear(); - } - } - - /** - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - private class CacheItem { - - private Object data; - - /** - * - * @param data - */ - public CacheItem(Object data) { - this.data = data; - } - - /** - * Gets the data. - * - * @return the data. - */ - public Object getData() { - return this.data; - }; - - /** - * Called by the garbage collector on an object when garbage collection - * determines that there are no more references to the object. A - * subclass overrides the finalize method to dispose of system resources - * or to perform other cleanup. - *

- * The finalize method is never invoked more than once by a Java virtual - * machine for any given object. - *

- * - * @throws Throwable - * the Exception raised by this method - * @see java.lang.Object#finalize() - */ - public void finalize() throws Throwable { - this.data = null; - super.finalize(); - } - - } - -} diff --git a/src/com/itmill/toolkit/terminal/web/TO BE DEPRECATED FROM 5.0 b/src/com/itmill/toolkit/terminal/web/TO BE DEPRECATED FROM 5.0 deleted file mode 100644 index 6a4a7bc868..0000000000 --- a/src/com/itmill/toolkit/terminal/web/TO BE DEPRECATED FROM 5.0 +++ /dev/null @@ -1,5 +0,0 @@ - -We'll selectively copy resources from "web/ajax-adapter" to gwt and deprecate web from 4.0 - -Although this directory will probably be deleted in future, please DO NOT YET delete resource from -this directory \ No newline at end of file diff --git a/src/com/itmill/toolkit/terminal/web/Theme.java b/src/com/itmill/toolkit/terminal/web/Theme.java deleted file mode 100644 index dbc879e7b4..0000000000 --- a/src/com/itmill/toolkit/terminal/web/Theme.java +++ /dev/null @@ -1,1243 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Stack; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParserFactory; -import org.xml.sax.helpers.DefaultHandler; -import org.xml.sax.SAXException; -import org.xml.sax.InputSource; -import org.xml.sax.XMLReader; -import org.xml.sax.Attributes; - -/** - * This class provides an interface to the meta-information regarding a - * particular theme. This entails for instanace the inheritance tree of the - * various xsl-template files, the different requirments that the theme imposes - * on the client browser, etc. - *

- * The WebAdapter uses themes to convert the UIDL description into client - * representation, typically HTML or XHTML. A theme consists of set of XSL - * template files which are used to perform XSL transform. - *

- *

- * XSL files are divided into sets, which can have requirements. A file set is - * included in transformation only if the given requirements are met. Following - * requirements are supported: - *

    - *
  • User-Agent HTTP header substring matching
  • - *
  • Markup language version
  • - *
  • JavaScript version
  • - *
- * Additionally following boolean operators may be applied to above - * requirements: - *
    - *
  • NOT
  • - *
  • AND
  • - *
  • OR
  • - *
- * The requirements are introduced in XML description file. See example below. - *

- *

- * The theme description is XML data, and it can be loaded from file or stream. - * The default filename is specified by Theme.DESCRIPTIONFILE. - * - *

- * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ -public class Theme extends DefaultHandler { - - /** - * Default description file name. - */ - public static final String DESCRIPTIONFILE = "description.xml"; - - private static final String TAG_THEME = "theme"; - - private static final String TAG_EXTENDS = "extends"; - - private static final String TAG_DESCRIPTION = "description"; - - private static final String TAG_FILE = "file"; - - private static final String TAG_FILESET = "fileset"; - - private static final String TAG_MODE = "mode"; - - private static final String TAG_MODES = "modes"; - - private static final String TAG_REQUIRE = "require"; - - private static final String TAG_SUPPORTS = "supports"; - - private static final String TAG_AUTHOR = "author"; - - private static final String TAG_AND = "and"; - - private static final String TAG_OR = "or"; - - private static final String TAG_NOT = "not"; - - private static final String ATTR_NAME = "name"; - - private static final String ATTR_THEME = "theme"; - - private static final String ATTR_EMAIL = "email"; - - private static final String ATTR_MODE = "mode"; - - private static final String ATTR_JAVASCRIPT = "javascript"; - - private static final String ATTR_AGENT = "agent"; - - private static final String ATTR_MARKUP = "markup"; - - public static final String MODE_AJAX = "ajax"; - - public static final String MODE_HTML = "html"; - - public static final String MODE_FALLBACK = MODE_HTML; - - public static final String MESSAGE_CONFIGURE_HELP = "You can provide themes by adding " - + "itmill-toolkit-x.y.z-themes.jar " - + "to WEB-INF/lib directory or adding " - + "theme files under WEB-INF/lib/themes directory." - + " Check also theme's description.xml."; - - /** - * Name of the theme. - */ - private String name; - - /** - * Theme description. - */ - private String description; - - /** - * Author of the theme. - */ - private Author author; - - /** - * Name of the theme, which this theme extends. - */ - private String parentTheme = null; - - /** - * Fileset of included XSL files. - */ - private Fileset files = null; - - /** - * Stack of fileset used while parsing XML. - */ - private Stack openFilesets = new Stack(); - - /** - * Stack of string buffers used while parsing XML. - */ - private Stack openStrings = new Stack(); - - /** - * Supported modes name-to-requirements. - */ - private LinkedHashMap supportedModes = new LinkedHashMap(); - - /** - * Currently open mode. - */ - private String currentlyOpenMode = null; - - /** - * Are we processing modes. - */ - private boolean modesListCurrentlyOpen = false; - - /** - * Is a NOT requirement element open. - */ - private boolean isNOTRequirementOpen = false; - - /** - * Currently open requirements while parsing. - */ - private Stack openRequirements = new Stack(); - - /** - * Creates a new instance using XML description file. Instantiate new theme, - * by loading the description from given File. - * - * @param descriptionFile - * the Description file. - * @throws FileNotFoundException - * Thrown if the given file is not found. - */ - public Theme(java.io.File descriptionFile) throws FileNotFoundException { - parse(new InputSource(new FileInputStream(descriptionFile))); - } - - /** - * Creates a new instance using XML description stream. Instantiate new - * theme, by loading the description from given InputSource. - * - * @param descriptionStream - * the XML input to parse - */ - public Theme(InputStream descriptionStream) { - try { - parse(new InputSource(descriptionStream)); - } finally { - try { - descriptionStream.close(); - } catch (IOException ignored) { - } - } - } - - /** - * Gets the preferred operating mode supported by this theme for given - * terminal. - * - * @param terminal - * the type of the web browser. - * @param themeSource - */ - public String getPreferredMode(WebBrowser terminal, ThemeSource themeSource) { - - // If no supported modes are declared, then we use parents preferred - // mode - if (parentTheme != null && supportedModes.keySet().isEmpty()) { - Theme parent = themeSource.getThemeByName(parentTheme); - if (parent == null) - throw new IllegalStateException("Parent theme '" + parentTheme - + "' is not found for theme '" + getName() + "'."); - return parent.getPreferredMode(terminal, themeSource); - } - - // Iterate and test the modes in order - for (Iterator i = supportedModes.keySet().iterator(); i.hasNext();) { - String mode = (String) i.next(); - if (supportsMode(mode, terminal, themeSource)) - return mode; - } - - return null; - } - - /** - * Tests if this theme suppors given mode. - * - * @param mode - * @param terminal - * the type of the web browser. - * @param themeSource - */ - public boolean supportsMode(String mode, WebBrowser terminal, - ThemeSource themeSource) { - - // Theme must explicitly support the given mode - RequirementCollection rc = (RequirementCollection) supportedModes - .get(mode); - - if (rc == null || !rc.isMet(terminal)) - return false; - - // All parents must also support the mode - if (parentTheme != null) { - Theme parent = themeSource.getThemeByName(parentTheme); - if (parent == null) - throw new IllegalStateException("Parent theme '" + parentTheme - + "' is not found for theme '" + getName() + "'. " - + MESSAGE_CONFIGURE_HELP); - if (!parent.supportsMode(mode, terminal, themeSource)) - return false; - } - - return true; - } - - /** - * Parses the XML data. - * - * @param descriptionSource - * the XML input source to parse. - */ - private synchronized void parse(InputSource descriptionSource) { - - // Clean-up parse time data - this.openStrings.clear(); - this.openFilesets.clear(); - this.openRequirements.clear(); - this.files = null; - - // Parse the Document - try { - XMLReader xr = SAXParserFactory.newInstance().newSAXParser() - .getXMLReader(); - - xr.setContentHandler(this); - xr.setErrorHandler(this); - - xr.parse(descriptionSource); - - return; - } catch (ParserConfigurationException e) { - e.printStackTrace(); - } catch (SAXException e) { - e.getException().printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } finally { - } - - } - - /** - * Parses start tag in XML stream. - * - * @see org.xml.sax.ContentHandler#startElement(java.lang.String, - * java.lang.String, java.lang.String, org.xml.sax.Attributes) - */ - public void startElement(String uri, String local, String qName, - Attributes atts) { - - if (TAG_THEME.equals(qName)) { - this.name = atts.getValue(ATTR_NAME); - } else if (TAG_DESCRIPTION.equals(qName)) { - this.description = "(none)"; - this.openStrings.push(new StringBuffer()); - } else if (TAG_EXTENDS.equals(qName)) { - String themeName = atts.getValue(ATTR_THEME); - if (this.name.equals(themeName)) - throw new IllegalArgumentException("Theme " + this.name - + " extends itself."); - if (parentTheme != null) - throw new IllegalArgumentException( - "Only one extends statement is allowed"); - this.parentTheme = themeName; - } else if (TAG_FILE.equals(qName)) { - File f = new File(atts.getValue(ATTR_NAME)); - if (this.openFilesets.isEmpty()) { - throw new IllegalStateException("Element '" + TAG_FILE - + "' must be within '" + TAG_FILESET + "' element."); - } - Fileset fs = (Fileset) this.openFilesets.peek(); - fs.addFile(f); - } else if (TAG_FILESET.equals(qName)) { - Fileset fs; - String mode = atts.getValue(ATTR_MODE); - if (mode != null && mode.length() == 0) - mode = null; - if (mode != null && !mode.equals(MODE_AJAX) - && !mode.equals(MODE_HTML)) - throw new IllegalStateException("Given mode '" + mode - + "' is not supported. (This version only supports '" - + MODE_HTML + "' and '" + MODE_AJAX + "')"); - fs = new Fileset(mode); - - // Uses the first fileset as root fileset - if (this.files == null) { - this.files = fs; - } - - // Adds inner filesets to parent - if (!this.openFilesets.isEmpty()) { - ((Fileset) this.openFilesets.peek()).addFile(fs); - } - - this.openFilesets.push(fs); - } else if (TAG_AUTHOR.equals(qName)) { - this.author = new Author(atts.getValue(ATTR_NAME), atts - .getValue(ATTR_EMAIL)); - } else if (TAG_MODES.equals(qName)) { - if (modesListCurrentlyOpen) - throw new IllegalStateException( - "Modes element can not be inside another modes element"); - modesListCurrentlyOpen = true; - } else if (TAG_MODE.equals(qName)) { - if (!modesListCurrentlyOpen) - throw new IllegalStateException( - "Mode elements must be placed inside modes element"); - if (currentlyOpenMode != null) - throw new IllegalStateException( - "No mode is allowed inside mode"); - String name = atts.getValue(ATTR_NAME); - if (name == null || name.length() == 0) - throw new IllegalStateException( - "Name is required for mode elements"); - this.currentlyOpenMode = name; - RequirementCollection rc = new AndRequirement(); - supportedModes.put(name, rc); - } - // Requirements - else if (TAG_REQUIRE.equals(qName)) { - if (currentlyOpenMode != null) { - RequirementCollection rc = (RequirementCollection) supportedModes - .get(this.currentlyOpenMode); - if (rc == null) - throw new IllegalStateException( - "Tried to add requirements to mode '" - + name - + "', but requirements set was not properly created. (internal error)"); - this.openRequirements.push(rc); - } else { - if (this.openFilesets.isEmpty()) { - throw new IllegalStateException("Element '" + TAG_REQUIRE - + "' must be within '" + TAG_FILESET + "' element."); - } - Fileset fs = (Fileset) this.openFilesets.peek(); - this.openRequirements.push(fs.getRequirements()); - } - } else if (TAG_SUPPORTS.equals(qName)) { - if (this.openFilesets.isEmpty() && currentlyOpenMode == null) { - throw new IllegalStateException("Element '" + TAG_REQUIRE - + "' must be within '" + TAG_FILESET + "' element."); - } - if (this.openRequirements.isEmpty()) { - throw new IllegalStateException("Element '" + TAG_SUPPORTS - + "' must be within '" + TAG_REQUIRE + "' element."); - } - this.addRequirements(atts, - (RequirementCollection) this.openRequirements.peek(), - this.isNOTRequirementOpen); - } else if (TAG_NOT.equals(qName)) { - this.isNOTRequirementOpen = true; - } else if (TAG_AND.equals(qName)) { - this.openRequirements.push(new AndRequirement()); - } else if (TAG_OR.equals(qName)) { - this.openRequirements.push(new OrRequirement()); - } - } - - /** - * Parses the end tag in XML stream. - * - * @see org.xml.sax.ContentHandler#endElement(String, String, String) - */ - public void endElement(String namespaceURI, String localName, String qName) - throws SAXException { - - if (TAG_FILESET.equals(qName)) { - this.openFilesets.pop(); - } else if (TAG_DESCRIPTION.equals(qName)) { - this.description = ((StringBuffer) this.openStrings.pop()) - .toString(); - } else if (TAG_REQUIRE.equals(qName)) { - this.openRequirements.pop(); - } else if (TAG_NOT.equals(qName)) { - this.isNOTRequirementOpen = false; - } else if (TAG_MODES.equals(qName)) { - this.modesListCurrentlyOpen = false; - } else if (TAG_MODE.equals(qName)) { - this.currentlyOpenMode = null; - } else if (TAG_OR.equals(qName) || TAG_AND.equals(qName)) { - RequirementCollection r = (RequirementCollection) openRequirements - .pop(); - if (openRequirements.size() < 1) - throw new IllegalStateException(); - ((RequirementCollection) openRequirements.peek()).addRequirement(r); - } - } - - /** - * Parses the character data in XML stream. - * - * @see org.xml.sax.ContentHandler#characters(char[], int, int) - */ - public void characters(char[] data, int start, int length) { - - // if stack is not ready, data is not content of recognized element - if (!this.openStrings.isEmpty()) { - ((StringBuffer) this.openStrings.peek()) - .append(data, start, length); - } else { - // read data which is not part of recognized element - } - } - - /** - * Adds all requirements specified in attributes to fileset. - * - * @param atts - * the Attribute set. - * @param requirements - * the Collection where to add requirement rules. - * @param applyNot - * the Should the meaning of these requirement be negated. - */ - private void addRequirements(Attributes atts, - RequirementCollection requirements, boolean applyNot) { - - // Creates temporary collection for requirements - Collection tmpReqs = new LinkedList(); - Requirement req = null; - - for (int i = 0; i < atts.getLength(); i++) { - req = null; - if (ATTR_JAVASCRIPT.equals(atts.getQName(i))) { - req = new JavaScriptRequirement(WebBrowser - .parseJavaScriptVersion(atts.getValue(i))); - } else if (ATTR_AGENT.equals(atts.getQName(i))) { - req = new AgentRequirement(atts.getValue(i)); - } else if (ATTR_MARKUP.equals(atts.getQName(i))) { - req = new MarkupLanguageRequirement(WebBrowser - .parseHTMLVersion(atts.getValue(i))); - } - // Adds to temporary requirement collection and clear reference - if (req != null) - tmpReqs.add(req); - } - - // Creates implicit AND requirement if more than one - // Rrequirements were specified in attributes - if (tmpReqs.size() > 1) { - req = new AndRequirement(tmpReqs); - } - - // Apply NOT rule if requested - if (applyNot) { - req = new NotRequirement(req); - } - - // Adds to requirements - requirements.addRequirement(req); - } - - /** - * Gets the list of all files in this theme. - * - * @return the List of filenames belonging to this theme. - */ - public List getFileNames() { - if (files == null) - return new LinkedList(); - return files.getFileNames(); - } - - /** - * Gets the list of file names matching WebBrowserType. - * - * @param terminal - * the type of the web browser. - * @param mode - * @return the list of filenames in this theme supporting the given - * terminal. - */ - public List getFileNames(WebBrowser terminal, String mode) { - if (files == null) - return new LinkedList(); - return this.files.getFileNames(terminal, mode); - } - - /** - * String representation of Theme object. Used for debugging purposes only. - * - * @see java.lang.Object#toString() - */ - public String toString() { - return this.name - + " author='" - + this.author - + "'" - + (parentTheme != null ? " inherits='" + parentTheme + "'" : "") - + " files={" + (files != null ? files.toString() : "null") - + "}"; - } - - /** - * Author information class. This class represents an single author of a - * theme package. Authors have name and contact email address properties. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - public class Author { - // name of the author. - private String name; - - // email address of the author. - private String email; - - /** - * Constructor for Author Information Class. - * - * @param name - * the name of the author. - * @param email - * the email address of the author. - */ - public Author(String name, String email) { - this.name = name; - this.email = email; - } - - /** - * Gets the name of the author. - * - * @return the Name of the author. - */ - public String getName() { - return this.name; - } - - /** - * Gets the email address of the author. - * - * @return the Email address of the author. - */ - public String getEmail() { - return this.email; - } - - /** - * @see java.lang.Object#toString() - */ - public String toString() { - return name + "(" + email + ")"; - } - } - - /** - * Generic requirement. Interface implemented by requirements introducing - * method for checking compability with given terminal. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - public interface Requirement { - - /** - * Checks that this requirement is met by given type of browser. - * - * @param terminal - * the type of the web browser. - * @return true if terminal is compatible with this - * rule,otherwise false. - * - */ - public boolean isMet(WebBrowser terminal); - - } - - /** - * Generic requirement collection interface. Requirement collection - * introducing methods for combining requirements into single requirement. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - public interface RequirementCollection extends Requirement { - - /** - * Adds the new requirement to this collection. - * - * @param requirement - * the Requirement to be added. - */ - public void addRequirement(Requirement requirement); - - /** - * Removes the requirement from this collection. - * - * @param requirement - * the Requirement to be removed. - */ - public void removeRequirement(Requirement requirement); - } - - /** - * Logical NOT requirement. Requirement implementing logical NOT operation. - * Wraps an another requirement and negates the meaning of it. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - public class NotRequirement implements Requirement { - private Requirement requirement; - - /** - * Create a new NOT requirement based on another requirement. - * - * @param requirement - * the requirement to be negated. - */ - public NotRequirement(Requirement requirement) { - this.requirement = requirement; - } - - /** - * Check that this requirement is met by given type of browser. - * - * @param terminal - * the type of the web browser. - * @return true if terminal is compatible with this - * rule,otherwise false. - * - */ - public boolean isMet(WebBrowser terminal) { - return !this.requirement.isMet(terminal); - } - - /** - * @see java.lang.Object#toString() - */ - public String toString() { - return "not(" + requirement + ")"; - } - - } - - /** - * Logical AND requirement. Implements a collection of requirements - * combining the included requirements using logical AND operation. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - public class AndRequirement implements RequirementCollection { - - private Collection requirements = new LinkedList(); - - public AndRequirement() { - } - - /** - * - * @param requirements - */ - public AndRequirement(Collection requirements) { - this.requirements.addAll(requirements); - } - - /** - * - * @param req1 - * @param req2 - */ - public AndRequirement(Requirement req1, Requirement req2) { - this.addRequirement(req1); - this.addRequirement(req2); - } - - /** - * Adds the new requirement to this collection. - * - * @see com.itmill.toolkit.terminal.web.Theme.RequirementCollection#addRequirement(com.itmill.toolkit.terminal.web.Theme.Requirement) - */ - public void addRequirement(Requirement requirement) { - this.requirements.add(requirement); - } - - /** - * Removes the requirement from this collection. - * - * @see com.itmill.toolkit.terminal.web.Theme.RequirementCollection#removeRequirement(com.itmill.toolkit.terminal.web.Theme.Requirement) - */ - public void removeRequirement(Requirement requirement) { - this.requirements.remove(requirement); - } - - /** - * Checks that all os the requirements in this collection are met. - * - * @param terminal - * the type of the web browser. - * @see Theme.Requirement#isMet(WebBrowser) - */ - public boolean isMet(WebBrowser terminal) { - for (Iterator i = this.requirements.iterator(); i.hasNext();) { - if (!((Requirement) i.next()).isMet(terminal)) { - return false; - } - } - return true; - } - - /** - * @see java.lang.Object#toString() - */ - public String toString() { - String str = ""; - for (Iterator i = this.requirements.iterator(); i.hasNext();) { - if (!"".equals(str)) - str += " AND "; - str += "(" + ((Requirement) i.next()).toString() + ")"; - } - return str; - } - - } - - /** - * Logical OR requirement. Implements a collection of requirements combining - * the included requirements using logical AND operation. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - public class OrRequirement implements RequirementCollection { - - private Collection requirements = new LinkedList(); - - public OrRequirement() { - } - - /** - * - * @param requirements - */ - public OrRequirement(Collection requirements) { - this.requirements.addAll(requirements); - } - - /** - * - * @param req1 - * @param req2 - */ - public OrRequirement(Requirement req1, Requirement req2) { - this.addRequirement(req1); - this.addRequirement(req2); - } - - /** - * Adds the new requirement to this collection. - * - * @see com.itmill.toolkit.terminal.web.Theme.RequirementCollection#addRequirement(com.itmill.toolkit.terminal.web.Theme.Requirement) - */ - public void addRequirement(Requirement requirement) { - this.requirements.add(requirement); - } - - /** - * Removes the requirement from this collection. - * - * @see com.itmill.toolkit.terminal.web.Theme.RequirementCollection#removeRequirement(com.itmill.toolkit.terminal.web.Theme.Requirement) - */ - public void removeRequirement(Requirement requirement) { - this.requirements.remove(requirement); - } - - /** - * Checks that some of the requirements in this collection is met. - * - * @param terminal - * the type of the web browser. - * @see Theme.Requirement#isMet(WebBrowser) - */ - public boolean isMet(WebBrowser terminal) { - for (Iterator i = this.requirements.iterator(); i.hasNext();) { - if (((Requirement) i.next()).isMet(terminal)) { - return true; - } - } - return false; - } - - /** - * @see java.lang.Object#toString() - */ - public String toString() { - String str = ""; - for (Iterator i = this.requirements.iterator(); i.hasNext();) { - if (!"".equals(str)) - str += " OR "; - str += "(" + ((Requirement) i.next()).toString() + ")"; - } - return str; - } - } - - /** - * HTTP user agent requirement This requirements is used to ensure that the - * User-Agent string provided in HTTP request headers contains given - * substring. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - public class AgentRequirement implements Requirement { - - private String agentSubstring; - - /** - * - * @param agentSubString - */ - public AgentRequirement(String agentSubString) { - this.agentSubstring = agentSubString; - } - - /** - * Checks that this requirement is met by given type of browser. - * - * @see com.itmill.toolkit.terminal.web.Theme.Requirement#isMet(com.itmill.toolkit.terminal.web.WebBrowser) - */ - public boolean isMet(WebBrowser terminal) { - return terminal.getBrowserApplication() - .indexOf(this.agentSubstring) >= 0; - } - - /** - * @see java.lang.Object#toString() - */ - public String toString() { - return this.agentSubstring; - } - } - - /** - * Javascript version requirement This requirement is used to ensure a - * certain level of JavaScript version support. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - public class JavaScriptRequirement implements Requirement { - - private WebBrowser.JavaScriptVersion requiredVersion; - - /** - * - * @param requiredVersion - */ - public JavaScriptRequirement( - WebBrowser.JavaScriptVersion requiredVersion) { - this.requiredVersion = requiredVersion; - } - - /** - * Checks that this requirement is met by given type of browser. - * - * @see com.itmill.toolkit.terminal.web.Theme.Requirement#isMet(com.itmill.toolkit.terminal.web.WebBrowser) - */ - public boolean isMet(WebBrowser terminal) { - if (terminal.getJavaScriptVersion().supports(this.requiredVersion)) - return true; - return false; - } - - /** - * @see java.lang.Object#toString() - */ - public String toString() { - return this.requiredVersion.toString(); - } - } - - /** - * Markup language version requirement. This requirement is used to ensure a - * certain level of Markup language version support. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - public class MarkupLanguageRequirement implements Requirement { - - private WebBrowser.MarkupVersion requiredVersion; - - /** - * - * @param requiredVersion - */ - public MarkupLanguageRequirement( - WebBrowser.MarkupVersion requiredVersion) { - this.requiredVersion = requiredVersion; - } - - /** - * Checks that this requirement is met by given type of browser. - * - * @see com.itmill.toolkit.terminal.web.Theme.Requirement#isMet(com.itmill.toolkit.terminal.web.WebBrowser) - */ - public boolean isMet(WebBrowser terminal) { - if (terminal.getMarkupVersion().supports(this.requiredVersion)) - return true; - return false; - - } - - /** - * @see java.lang.Object#toString() - */ - public String toString() { - return this.requiredVersion.toString(); - } - - } - - /** - * Theme XSL file description Description of a single XSL file included a - * theme. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - public class File { - - private String name; - - /** - * Creates a new file. - * - * @param name - * the Name of the file. - */ - public File(String name) { - this.name = name; - } - - /** - * Gets the name of the file. The file name is relative and unique - * within a theme. - * - * @return the Name of the file. - */ - public String getName() { - return this.name; - } - - /** - * Does this file support the given terminal. Single file requirements - * are not supported and therefore this always returns true. - * - * @param terminal - * the type of the web browser. - * @return Always returns true. - * @see Theme.Fileset - */ - public boolean supports(WebBrowser terminal) { - return true; - } - - /** - * @see java.lang.Object#toString() - */ - public String toString() { - return this.getName(); - } - - } - - /** - * A recursive set of files sharing the same requirements. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - public class Fileset extends File { - - private RequirementCollection requirements = new AndRequirement(); - - private Collection files = new LinkedList(); - - private String mode; - - /** - * Creates a new empty fileset. - * - * @param mode - * - */ - public Fileset(String mode) { - super(null); - this.mode = mode; - } - - /** - * Adds a file into fileset. - * - * @param file - * the file to add. - */ - private void addFile(File file) { - this.files.add(file); - } - - /** - * Gets the requirements in this fileset. - * - * @return the requirements. - */ - private RequirementCollection getRequirements() { - return this.requirements; - } - - /** - * Gets the list of all files in this theme. - * - * @return the list of filenames. - */ - public List getFileNames() { - - List list = new LinkedList(); - - for (Iterator i = this.files.iterator(); i.hasNext();) { - File f = (File) i.next(); - - // Recursively add included filesets - if (f instanceof Fileset) { - list.addAll(((Fileset) f).getFileNames()); - } else { - list.add(f.getName()); - } - } - return list; - - } - - /** - * Gets the list of file names matching WebBrowserType. - * - * @param terminal - * the type of the web browser. - * @param mode - * @return the list of filenames supporting the given terminal. - */ - public List getFileNames(WebBrowser terminal, String mode) { - - List list = new LinkedList(); - - // If this set is not supported by the terminal or is explicitly set - // into - // another mode, no files are given - if (!this.supports(terminal) - || (this.mode != null && !this.mode.equals(mode))) - return list; - - for (Iterator i = this.files.iterator(); i.hasNext();) { - File f = (File) i.next(); - - // Recursively add included filesets if they are - // supported - if (f instanceof Fileset) { - list.addAll(((Fileset) f).getFileNames(terminal, mode)); - - } else { - list.add(f.getName()); - } - } - return list; - } - - /** - * Does this file support the given terminal. - * - * @terminal the type of the web browser. - * @return True if fileset supports the given browser. False otherwise. - */ - public boolean supports(WebBrowser terminal) { - if (requirements.isMet(terminal)) - return true; - return false; - } - - /** - * @see java.lang.Object#toString() - */ - public String toString() { - return "name=[" + this.getName() + "] requires=[" - + this.requirements + "] files=[" + files + "]"; - } - } - - /** - * Gets the author of this theme. - * - * @return the Author of the theme. - */ - public Author getAuthor() { - return author; - } - - /** - * Gets the name of this theme. - * - * @return the Name of the theme. - */ - public String getName() { - return name; - } - - /** - * Gets the name of the parent theme. - * - * @return the name of the parent theme. - */ - public String getParent() { - return parentTheme; - } - - /** - * Gets the theme description. - * - * @return the theme description. - */ - public String getDescription() { - return description; - } - -} diff --git a/src/com/itmill/toolkit/terminal/web/ThemeFunctionLibrary.java b/src/com/itmill/toolkit/terminal/web/ThemeFunctionLibrary.java deleted file mode 100644 index e03f6b3d91..0000000000 --- a/src/com/itmill/toolkit/terminal/web/ThemeFunctionLibrary.java +++ /dev/null @@ -1,606 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import com.itmill.toolkit.Application; -import com.itmill.toolkit.terminal.ThemeResource; -import com.itmill.toolkit.ui.FrameWindow; -import com.itmill.toolkit.ui.Window; - -import java.text.DateFormatSymbols; -import java.util.Calendar; -import java.util.Collection; -import java.util.GregorianCalendar; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.Map; -import java.util.Vector; - -import javax.servlet.http.HttpSession; - -/** - * This a function library that can be used from the theme XSL-files. It - * provides easy access to current application, window, theme, webbrowser and - * session. The internal threadlocal state must be maintained by the webadapter - * in order to guarantee that it works. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - -public class ThemeFunctionLibrary { - - static private final int APPLICATION = 0; - - static private final int WINDOW = 1; - - static private final int WEBBROWSER = 2; - - static private final int SESSION = 3; - - static private final int WEBADAPTERSERVLET = 4; - - static private final int THEME = 5; - - static private ThreadLocal state = new ThreadLocal(); - - /** - * - * @param application - * @param window - * @param webBrowser - * @param session - * @param webAdapterServlet - * @param theme - */ - static protected void setState(Application application, Window window, - WebBrowser webBrowser, HttpSession session, - ApplicationServlet webAdapterServlet, String theme) { - state.set(new Object[] { application, window, webBrowser, session, - webAdapterServlet, theme }); - } - - static protected void cleanState() { - state.set(null); - } - - /** - * Returns a reference to the application object associated with the session - * that the call came from. - */ - static public Application application() { - try { - return (Application) ((Object[]) state.get())[APPLICATION]; - } catch (NullPointerException e) { - throw new IllegalStateException(); - } - } - - /** - * Returns a reference to the current window object associated with the - * session that the call came from. - */ - static public Window window() { - try { - return (Window) ((Object[]) state.get())[WINDOW]; - } catch (NullPointerException e) { - throw new IllegalStateException(); - } - } - - /** - * Returns a reference to the browser object associated with the session - * that the call came from. - */ - static public WebBrowser browser() { - try { - return (WebBrowser) ((Object[]) state.get())[WEBBROWSER]; - } catch (NullPointerException e) { - throw new IllegalStateException(); - } - } - - /** - * Returns a reference to the current servlet http session object that is - * associated with the session that the call came from. - */ - static public HttpSession session() { - try { - return (HttpSession) ((Object[]) state.get())[SESSION]; - } catch (NullPointerException e) { - throw new IllegalStateException(); - } - } - - /** - * Returns a reference to the current theme name that is associated with the - * session that the call came from. - */ - static public String theme() { - try { - return (String) ((Object[]) state.get())[THEME]; - } catch (NullPointerException e) { - throw new IllegalStateException(); - } - } - - /** - * Returns an URI to the named resource from the named theme. - * - * @param resource - * @param theme - */ - static public String resource(String resource, String theme) { - try { - return ((ApplicationServlet) ((Object[]) state.get())[WEBADAPTERSERVLET]) - .getResourceLocation(theme, new ThemeResource(resource)); - } catch (NullPointerException e) { - throw new IllegalStateException(); - } - } - - /** - * Returns an URI to the named resource. - * - * @param resource - */ - static public String resource(String resource) { - try { - return ((ApplicationServlet) ((Object[]) state.get())[WEBADAPTERSERVLET]) - .getResourceLocation(theme(), new ThemeResource(resource)); - } catch (NullPointerException e) { - throw new IllegalStateException(); - } - } - - /** - * Generates the JavaScript for page that performs client-side combility - * checks. - */ - static public boolean probeClient() { - return (browser().performClientCheck() && !browser() - .isClientSideChecked()); - } - - /** - * Generates the JavaScript for page header that handles window refreshing, - * opening and closing. - * - * Generates script that: - *
    - *
  • Requests that all windows that need repaint be reloaded
  • - *
  • Sets the window name
  • - *
  • Closes window if it is set to be closed
  • - *
      - * - * @return - */ - static public String windowScript() { - return generateWindowScript( - window(), - application(), - (ApplicationServlet) ((Object[]) state.get())[WEBADAPTERSERVLET], - browser()); - } - - /** - * - * @param window - * @param app - * @param wa - * @param browser - * @return - */ - static protected String generateWindowScript(Window window, - Application app, ApplicationServlet wa, WebBrowser browser) { - - StringBuffer script = new StringBuffer(); - LinkedList update = new LinkedList(); - - // Adds all the windows needto update list - Map dirtyWindows = wa != null ? wa.getDirtyWindows(app) : null; - if (dirtyWindows != null) - for (Iterator i = dirtyWindows.keySet().iterator(); i.hasNext();) { - Window w = (Window) i.next(); - if (w != window) { - if (w instanceof FrameWindow) - update.addFirst(w); - else - update.addLast(w); - } - } - - // Removes all windows that are in frames, of such frame windows that - // will be updated anyway - Object[] u = update.toArray(); - if (u.length > 0 && (window != null && window instanceof FrameWindow)) - u[u.length - 1] = window; - for (int i = 0; i < u.length; i++) { - try { - FrameWindow w = (FrameWindow) u[i]; - LinkedList framesets = new LinkedList(); - framesets.add(w.getFrameset()); - while (!framesets.isEmpty()) { - FrameWindow.Frameset fs = (FrameWindow.Frameset) framesets - .removeFirst(); - for (Iterator j = fs.getFrames().iterator(); j.hasNext();) { - FrameWindow.Frame f = (FrameWindow.Frame) j.next(); - if (f instanceof FrameWindow.Frameset) - framesets.add(f); - else if (f.getWindow() != null) { - update.remove(f.getWindow()); - wa.removeDirtyWindow(app, f.getWindow()); - } - } - } - } catch (ClassCastException ignored) { - } - } - - // Sets window name - if (window != null) { - script.append("window.name = \"" + getWindowTargetName(app, window) - + "\";\n"); - } - - // Generates window updatescript - for (Iterator i = update.iterator(); i.hasNext();) { - Window w = (Window) i.next(); - script.append(getWindowRefreshScript(app, w, browser)); - - wa.removeDirtyWindow(app, w); - - // Windows that are closed immediately are "painted" now - if (w.getApplication() == null || !w.isVisible()) - w.requestRepaintRequests(); - } - - // Closes current window if it is not visible - if (window == null || !window.isVisible()) - script.append("window.close();\n"); - - return script.toString(); - } - - /** - * Returns an unique target name for a given window name. - * - * @param application - * @param window - * the Name of the window. - * @return An unique ID for window target. - */ - static public String getWindowTargetName(Application application, - Window window) { - try { - return "" + application.hashCode() + "_" + window.getName(); - } catch (NullPointerException e) { - throw new IllegalStateException(); - } - } - - /** - * Returns an unique target name for current window. - * - * @return An unique ID for window target. - */ - static public String getWindowTargetName() { - return getWindowTargetName(application(), window()); - } - - /** - * Returns an unique target name for current window. - * - * @param name - * the name of the window. - * @return An unique ID for window target. - */ - static public String getWindowTargetName(String name) { - Window w = application().getWindow(name); - if (w != null) - return getWindowTargetName(application(), w); - else - return name; - } - - /* Static mapping for 0 to be sunday. */ - private static int[] weekdays = new int[] { Calendar.SUNDAY, - Calendar.MONDAY, Calendar.TUESDAY, Calendar.WEDNESDAY, - Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY }; - - /** - * Returns the country and region code for current application locale. - * - * @return the language Country code of the current application locale. - * @see Locale#getCountry() - */ - static public String getLocaleCountryId() { - try { - Application app = (Application) ((Object[]) state.get())[APPLICATION]; - return app.getLocale().getCountry(); - } catch (NullPointerException e) { - throw new IllegalStateException(); - } - } - - /** - * Returns the language code for current application locale. - * - * @return the Language code for current application locale. - * @see Locale#getLanguage() - */ - static public String getLocaleLanguageId() { - try { - Application app = (Application) ((Object[]) state.get())[APPLICATION]; - return app.getLocale().getLanguage(); - } catch (NullPointerException e) { - throw new IllegalStateException(); - } - } - - /** - * Gets the name of first day of the week. - * - * @return - */ - static public int getFirstDayOfWeek() { - try { - Application app = (Application) ((Object[]) state.get())[APPLICATION]; - Calendar cal = new GregorianCalendar(app.getLocale()); - int first = cal.getFirstDayOfWeek(); - for (int i = 0; i < 7; i++) { - if (first == weekdays[i]) - return i; - } - return 0; // default to sunday - } catch (NullPointerException e) { - throw new IllegalStateException(); - } - } - - /** - * Gets the name for week day. - * - * @param dayOfWeek - * the Number of week day. 0 sunday, 1 monday, ... - * @return the Name of week day in applications current locale. - */ - static public String getShortWeekday(int dayOfWeek) { - try { - Application app = (Application) ((Object[]) state.get())[APPLICATION]; - DateFormatSymbols df = new DateFormatSymbols(app.getLocale()); - return df.getShortWeekdays()[weekdays[dayOfWeek]]; - } catch (NullPointerException e) { - throw new IllegalStateException(); - } - } - - /** - * Gets the short name for month. - * - * @param month - * the Number of month. 0 is January, 1 is February, and so on. - * @return the Name of month in applications current locale. - */ - static public String getShortMonth(int month) { - try { - Application app = (Application) ((Object[]) state.get())[APPLICATION]; - DateFormatSymbols df = new DateFormatSymbols(app.getLocale()); - String monthName = df.getShortMonths()[month]; - return monthName; - } catch (NullPointerException e) { - throw new IllegalStateException(); - } - } - - /** - * Gets the name for month. - * - * @param month - * the Number of month. 0 is January, 1 is February, and so on. - * @return the Name of month in applications current locale. - */ - static public String getMonth(int month) { - try { - Application app = (Application) ((Object[]) state.get())[APPLICATION]; - DateFormatSymbols df = new DateFormatSymbols(app.getLocale()); - String monthName = df.getMonths()[month]; - return monthName; - } catch (NullPointerException e) { - throw new IllegalStateException(); - } - } - - /** - * Gets Form Action URL for the requested window. - * - *

      - * This returns the action for the window main form. This action can be set - * through WebApplicationContect setWindowFormAction method.. - *

      - * - * @return the Form action for the current window. - */ - static public String getFormAction() { - - Window win = window(); - Application app = application(); - - return ((WebApplicationContext) app.getContext()) - .getWindowFormAction(win); - } - - /** - * Generates the links for CSS files to be included in html head. - * - * @return - */ - static public String getCssLinksForHead() { - ApplicationServlet as = (ApplicationServlet) ((Object[]) state.get())[WEBADAPTERSERVLET]; - Theme t = as.getThemeSource().getThemeByName(theme()); - - // Also iterate parent themes - Vector themes = new Vector(); - themes.add(t); - while (t.getParent() != null) { - String parentName = t.getParent(); - t = as.getThemeSource().getThemeByName(parentName); - themes.add(t); - } - - // Generates links - StringBuffer links = new StringBuffer(); - for (int k = themes.size() - 1; k >= 0; k--) { - Collection allFiles = ((Theme) themes.get(k)).getFileNames( - browser(), Theme.MODE_HTML); - for (Iterator i = allFiles.iterator(); i.hasNext();) { - String file = (String) i.next(); - if (file.endsWith(".css")) { - links - .append("\n"); - } - } - } - - return links.toString(); - } - - /** - * Generates the links for JavaScript files to be included in html head. - * - * @return - */ - static public String getJavaScriptLinksForHead() { - ApplicationServlet as = (ApplicationServlet) ((Object[]) state.get())[WEBADAPTERSERVLET]; - Theme t = as.getThemeSource().getThemeByName(theme()); - - // Also iterate parent themes - Vector themes = new Vector(); - themes.add(t); - while (t.getParent() != null) { - String parentName = t.getParent(); - t = as.getThemeSource().getThemeByName(parentName); - themes.add(t); - } - - // Generates links - StringBuffer links = new StringBuffer(); - for (int k = themes.size() - 1; k >= 0; k--) { - Collection allFiles = ((Theme) themes.get(k)).getFileNames( - browser(), Theme.MODE_HTML); - for (Iterator i = allFiles.iterator(); i.hasNext();) { - String file = (String) i.next(); - if (file.endsWith(".js")) { - links.append("\n"); - } - } - } - - return links.toString(); - } - - /** - * Generates the JavaScript for updating given window. - * - * @param application - * @param window - * @param browser - * @return - */ - static protected String getWindowRefreshScript(Application application, - Window window, WebBrowser browser) { - - if (application == null) - return ""; - - if (window == null) - return ""; - - if (window == null) - return ""; - - // If window is closed or hidden - if (window.getApplication() == null || !window.isVisible()) - return "win = window.open(\"\",\"" - + getWindowTargetName(application, window) + "\");\n " - + "if (win != null) { win.close(); }\n"; - - String url = window.getURL().toString(); - - String features = "dependent=yes,"; - int width = window.getWidth(); - int height = window.getHeight(); - if (width >= 0) - features += "width=" + width; - if (height >= 0) - features += ((features.length() > 0) ? "," : "") + "height=" - + height; - switch (window.getBorder()) { - case Window.BORDER_NONE: - features += ((features.length() > 0) ? "," : "") - + "toolbar=0,location=0,menubar=0,status=0,resizable=1,scrollbars=" - + (window.isScrollable() ? "1" : "0"); - break; - case Window.BORDER_MINIMAL: - features += ((features.length() > 0) ? "," : "") - + "toolbar=1,location=0,menubar=0,status=1,resizable=1,scrollbars=" - + (window.isScrollable() ? "1" : "0"); - break; - case Window.BORDER_DEFAULT: - features += ((features.length() > 0) ? "," : "") - + "toolbar=1,location=1,menubar=1,status=1,resizable=1,scrollbars=" - + (window.isScrollable() ? "1" : "0"); - break; - } - - String script = "win = window.open(\"\",\"" - + getWindowTargetName(application, window) + "\",\"" + features - + "\");\n" + "if (win != null) {" + "var form = null;"; - - if (browser != null - && (browser.getJavaScriptVersion().supports( - WebBrowser.JAVASCRIPT_1_5) || browser - .getJavaScriptVersion() - .supports(WebBrowser.JSCRIPT_1_0))) { - script += "try { form = win.document.forms[\"itmill\"]; if (typeof form == 'undefined') form = win.document.forms[\"millstone\"];" - + "} catch (e) { form = null;}"; - } else { - script += "form = win.document.forms[\"itmill\"]; if (typeof form == 'undefined') form = win.document.forms[\"millstone\"];"; - } - - script += "if (form != null) {" + "form.submit();" - + "} else {win.location.href = \"" + url + "\";}}"; - - return script; - } -} diff --git a/src/com/itmill/toolkit/terminal/web/ThemeSource.java b/src/com/itmill/toolkit/terminal/web/ThemeSource.java deleted file mode 100644 index c2ff054683..0000000000 --- a/src/com/itmill/toolkit/terminal/web/ThemeSource.java +++ /dev/null @@ -1,183 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import java.io.InputStream; -import java.util.Collection; - -/** - * Interface implemented by theme sources. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ -public interface ThemeSource { - - /** - * Gets the name of the ThemeSource. - * - * @return the Name of the theme source. - */ - public String getName(); - - /** - * Gets the XSL stream for the specified theme and web-browser type. Returns - * the XSL templates, which are used to process the UIDL data. The - * type parameter is used to limit the templates, which are - * returned based on the theme fileset requirements. - *

      - * Note : This implicitly operates in xslt mode. - *

      - * - * @param theme - * the Theme, which XSL should be returned. - * @param type - * the type of the current client. - * @return Collection of ThemeSource.XSLStream objects. - * @throws ThemeException - * If the resource is not found or there was some problem - * finding the resource. - * @see Theme - */ - public Collection getXSLStreams(Theme theme, WebBrowser type) - throws ThemeException; - - /** - * Gets the last modification time, used to reload theme on changes. - * - * @return the Last modification time of the theme source. - */ - public long getModificationTime(); - - /** - * Gets the input stream for the resource with the specified resource id. - * - * @param resourceId - * the resource id. - * @return Stream where the resource can be read. - * @throws ThemeException - * If the resource is not found or there was some problem - * finding the resource. - */ - public InputStream getResource(String resourceId) throws ThemeException; - - /** - * Gets the list of themes in the theme source. - * - * @return the List of themes included in the theme source. - */ - public Collection getThemes(); - - /** - * Returns the Theme instance by name. - * - * @param name - * the Theme name. - * @return the Theme instance matching the name, or null if not found. - */ - public Theme getThemeByName(String name); - - /** - * ThemeException is thrown by classes implementing the - * ThemeSource interface if some error occurs during - * processing. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - public class ThemeException extends Exception { - - private static final long serialVersionUID = -7823850742197580285L; - - /** - * Creates a new theme exception. - * - * @param message - * the Error message. - */ - public ThemeException(String message) { - super(message); - } - - /** - * Creates a new theme exception. - * - * @param message - * the error message. - * @param cause - * the cause of the exception. - */ - public ThemeException(String message, Throwable cause) { - super(message, cause); - } - } - - /** - * Wrapper class for XSL InputStreams. - */ - public class XSLStream { - private String id; - - private InputStream stream; - - /** - * - * @param id - * @param stream - * the input stream. - */ - public XSLStream(String id, InputStream stream) { - this.id = id; - this.stream = stream; - } - - /** - * Returns id of this stream. - * - * @return the id of the stream. - */ - public String getId() { - return id; - } - - /** - * Returns the actual XSL Stream. - * - * @return the XSL Stream. - */ - public InputStream getStream() { - return stream; - } - - } -} diff --git a/src/com/itmill/toolkit/terminal/web/UIDLTransformer.java b/src/com/itmill/toolkit/terminal/web/UIDLTransformer.java deleted file mode 100644 index 694f068045..0000000000 --- a/src/com/itmill/toolkit/terminal/web/UIDLTransformer.java +++ /dev/null @@ -1,670 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.SAXParseException; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.XMLReaderFactory; - -import com.itmill.toolkit.terminal.PaintException; - -import java.io.BufferedOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.StringReader; -import java.util.Collection; -import java.util.LinkedList; -import java.util.Hashtable; -import java.util.Iterator; - -import javax.xml.transform.sax.SAXSource; -import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.ErrorListener; -import javax.xml.transform.SourceLocator; -import javax.xml.transform.OutputKeys; - -/** - * Class implementing the UIDLTransformer. - * - * The transformer should not be created directly; it should be contructed using - * getTransformer provided by UIDLTransformerFactory. - * - * After the transform has been done, the transformer can be recycled with - * releaseTransformer by UIDLTransformerFactory. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - -public class UIDLTransformer { - - /** - * XSLT factory. - */ - protected static javax.xml.transform.TransformerFactory xsltFactory; - static { - xsltFactory = javax.xml.transform.TransformerFactory.newInstance(); - if (xsltFactory == null) - throw new RuntimeException("Could not instantiate " - + "transformer factory. Maybe XSLT processor is " - + "not included in classpath."); - } - - /** - * Source of the transform containing UIDL. - */ - private WebPaintTarget paintTarget; - - /** - * Holds the type of the transformer. - */ - private UIDLTransformerType transformerType; - - /** - * Prepared XSLT transformer for UIDL transformations. - */ - private javax.xml.transform.Transformer uidlTransformer; - - /** - * Error handled used. - */ - private TransformerErrorHandler errorHandler; - - /** - * Theme repository used for late error reporting. - */ - private ThemeSource themeSource; - - private ApplicationServlet webAdapterServlet; - - /** - * UIDLTransformer constructor. - * - * @param type - * the Type of the transformer. - * @param themes - * the theme implemented by the transformer. - * @param webAdapterServlet - * the Adapter servlet. - * @throws UIDLTransformerException - * UIDLTransformer exception is thrown, if the transform can not - * be created. - */ - public UIDLTransformer(UIDLTransformerType type, ThemeSource themes, - ApplicationServlet webAdapterServlet) - throws UIDLTransformerException { - this.transformerType = type; - this.themeSource = themes; - this.webAdapterServlet = webAdapterServlet; - - // Registers the error handler - errorHandler = new TransformerErrorHandler(); - xsltFactory.setErrorListener(errorHandler); - - try { - - // Creates XML Reader to be used by - // XSLReader as the actual parser object. - XMLReader parser = XMLReaderFactory.createXMLReader(); - - // Creates XML reader for concatenating - // multiple XSL files as one. - - XMLReader xmlReader = new XSLReader(parser, themes.getXSLStreams( - type.getTheme(), type.getWebBrowser())); - - xmlReader.setErrorHandler(errorHandler); - - // Creates own SAXSource using a dummy inputSource. - SAXSource source = new SAXSource(xmlReader, new InputSource()); - uidlTransformer = xsltFactory.newTransformer(source); - - if (uidlTransformer != null) { - - // Registers transformer error handler - uidlTransformer.setErrorListener(errorHandler); - - // Ensures HTML output - uidlTransformer.setOutputProperty(OutputKeys.METHOD, "html"); - - // Ensures no indent - uidlTransformer.setOutputProperty(OutputKeys.INDENT, "no"); - } - - // Checks if transform itself failed, meaning either - // UIDL error or error in XSL/T semantics (like XPath) - if (errorHandler.hasFatalErrors()) { - throw new UIDLTransformerException( - "XSL Transformer creation failed", errorHandler - .getFirstFatalError(), errorHandler - .getUIDLErrorReport() - + "

      " - + errorHandler.getXSLErrorReport(themeSource, - transformerType)); - } - - } catch (Exception e) { - // Pass the new XHTML coded error forwards - throw new UIDLTransformerException(e.toString(), e, errorHandler - .getXSLErrorReport(themeSource, transformerType)); - } - } - - /** - * Gets the type of the transformer. - * - * @return the Type of the transformer. - */ - public UIDLTransformerType getTransformerType() { - return this.transformerType; - } - - /** - * Attaches the output stream to transformer and get corresponding - * UIDLStream for writing UI description language trough transform to given - * output. - * - * @param variableMap - * the variable map used for UIDL creation. - * @return returns UI description language stream, that can be used for - * writing UIDL to transformer. - */ - public WebPaintTarget getPaintTarget(HttpVariableMap variableMap) { - - try { - paintTarget = new WebPaintTarget(variableMap, transformerType, - webAdapterServlet, transformerType.getTheme()); - } catch (PaintException e) { - throw new IllegalArgumentException( - "Failed to instantiate new WebPaintTarget: " + e); - } - return paintTarget; - } - - /** - * Resets the transformer, before it can be used again. This also interrupts - * any ongoing transform and thus should not be called before the transform - * is ready. This is automatically called by the UIDLTransformFactory, when - * the UIDLTransformer has been released. - * - * @see UIDLTransformerFactory#releaseTransformer(UIDLTransformer) - */ - protected void reset() { - if (paintTarget != null) { - try { - paintTarget.close(); - } catch (PaintException e) { - // Ignore this exception - } - paintTarget = null; - } - if (errorHandler != null) - errorHandler.clear(); - } - - /** - * Transforms the UIDL to HTML and output to the OutputStream. - * - * @param outputStream - * the output stream to render to. - * @throws UIDLTransformerException - * UIDLTransformer exception is thrown, if the transform can not - * be created. - */ - public void transform(OutputStream outputStream) - throws UIDLTransformerException { - - StreamResult result = new StreamResult(new BufferedOutputStream( - outputStream)); - - // XSL Transform - try { - InputSource uidl = new InputSource(new StringReader(paintTarget - .getUIDL())); - XMLReader reader = org.xml.sax.helpers.XMLReaderFactory - .createXMLReader(); - reader.setErrorHandler(this.errorHandler); - - // Validates if requested. We validate the UIDL separately, - // toget the SAXExceptions instead of TransformerExceptions. - // This is required to get the line numbers right. - /* - * FIXME: Disable due abnormalities in DTD handling. if - * (webAdapterServlet.isDebugMode()) { reader.setFeature( - * "http://xml.org/sax/features/validation", true); - * reader.parse(uidl); uidl = new InputSource(new - * StringReader(paintTarget.getUIDL())); } - */ - SAXSource source = new SAXSource(reader, uidl); - - // TODO HTML mode under heavy load provides - // java.net.SocketException: broken pipe - uidlTransformer.transform(source, result); - } catch (Exception e) { - // XSL parsing failed. Pass the new XHTML coded error forwards - throw new UIDLTransformerException(e.toString(), e, errorHandler - .getUIDLErrorReport()); - } - - // Checks if transform itself failed, meaning either - // UIDL error or error in XSL/T semantics (like XPath) - if (errorHandler.hasFatalErrors()) { - throw new UIDLTransformerException("UIDL Transform failed", - errorHandler.getFirstFatalError(), errorHandler - .getUIDLErrorReport() - + "

      " - + errorHandler.getXSLErrorReport(themeSource, - transformerType)); - } - } - - /** - * - * - * - */ - protected class TransformerErrorHandler implements ErrorListener, - org.xml.sax.ErrorHandler { - - LinkedList errors = new LinkedList(); - - LinkedList warnings = new LinkedList(); - - LinkedList fatals = new LinkedList(); - - Hashtable rowToErrorMap = new Hashtable(); - - Hashtable errorToRowMap = new Hashtable(); - - /** - * - * @return - */ - public boolean hasNoErrors() { - return errors.isEmpty() && warnings.isEmpty() && fatals.isEmpty(); - } - - /** - * - * @return - */ - public boolean hasFatalErrors() { - return !fatals.isEmpty(); - } - - /** - * - * - */ - public void clear() { - errors.clear(); - warnings.clear(); - fatals.clear(); - } - - /** - * @see java.lang.Object#toString() - */ - public String toString() { - return getHTMLErrors("Fatal Errors", fatals) + "
      " - + getHTMLErrors("Errors", errors) + "
      " - + getHTMLErrors("Warnings", warnings) + "
      "; - } - - /** - * - * @param title - * @param l - * @return - */ - private String getHTMLErrors(String title, LinkedList l) { - String r = ""; - r = "" + title + "
      "; - if (l.size() > 0) { - for (Iterator i = l.iterator(); i.hasNext();) { - Exception e = (Exception) i.next(); - if (e instanceof javax.xml.transform.TransformerException) { - Integer line = (Integer) errorToRowMap.get(e); - r += " - " - + WebPaintTarget - .escapeXML(((javax.xml.transform.TransformerException) e) - .getMessage()); - Throwable cause = ((javax.xml.transform.TransformerException) e) - .getException(); - - // Append cause if available - if (cause != null) { - r += ": " - + WebPaintTarget.escapeXML(cause - .getMessage()); - } - r += line != null ? " (line:" + line.intValue() + ")" - : " (line unknown)"; - r += "
      \n"; - } else { - Integer line = (Integer) errorToRowMap.get(e); - r += " - " + WebPaintTarget.escapeXML(e.toString()); - r += line != null ? " (line:" + line.intValue() + ")" - : " (line unknown)"; - r += "
      \n"; - - } - } - } - return r; - } - - /** - * @see javax.xml.transform.ErrorListener#error(TransformerException) - */ - public void error(javax.xml.transform.TransformerException exception) { - if (exception != null) { - errors.addLast(exception); - SourceLocator l = exception.getLocator(); - if (l != null) { - rowToErrorMap.put(new Integer( - ((XSLReader.XSLStreamLocator) l).getLineNumber()), - exception); - errorToRowMap.put(exception, new Integer( - ((XSLReader.XSLStreamLocator) l).getLineNumber())); - } - } - } - - /** - * @see javax.xml.transform.ErrorListener#fatalError(TransformerException) - */ - public void fatalError( - javax.xml.transform.TransformerException exception) { - if (exception != null) { - fatals.addLast(exception); - SourceLocator l = exception.getLocator(); - if (l != null) { - rowToErrorMap - .put(new Integer(l.getLineNumber()), exception); - errorToRowMap - .put(exception, new Integer(l.getLineNumber())); - } - } - } - - /** - * @see javax.xml.transform.ErrorListener#warning(TransformerException) - */ - public void warning(javax.xml.transform.TransformerException exception) { - if (exception != null) { - warnings.addLast(exception); - SourceLocator l = exception.getLocator(); - if (l != null) { - rowToErrorMap - .put(new Integer(l.getLineNumber()), exception); - errorToRowMap - .put(exception, new Integer(l.getLineNumber())); - } - } - } - - /** - * Gets the formated error report on XSL. - * - * @param themes - * @param type - */ - public String getXSLErrorReport(ThemeSource themes, - UIDLTransformerType type) { - - // Recreates the XSL for error reporting - StringBuffer readBuffer = new StringBuffer(); - try { - Collection c = themes.getXSLStreams(type.getTheme(), type - .getWebBrowser()); - for (Iterator i = c.iterator(); i.hasNext();) { - - java.io.InputStream is = ((ThemeSource.XSLStream) i.next()) - .getStream(); - byte[] buffer = new byte[1024]; - int read = 0; - while ((read = is.read(buffer)) >= 0) - readBuffer.append(new String(buffer, 0, read)); - } - } catch (IOException ignored) { - - } catch (ThemeSource.ThemeException ignored) { - - } - - String xsl = "XSL Source not avaialable"; - if (readBuffer != null) - xsl = readBuffer.toString(); - - StringBuffer sb = new StringBuffer(); - - // Print formatted UIDL with errors embedded - - int row = 0; - int prev = 0; - int index = 0; - int errornro = 0; - boolean lastLineWasEmpty = false; - - sb.append(toString()); - sb - .append("" - + "Go to first error" - + "" - + "\n"); - - while ((index = xsl.indexOf('\n', prev)) >= 0) { - String line = xsl.substring(prev, index); - prev = index + 1; - row++; - - Exception exp = (Exception) rowToErrorMap.get(new Integer(row)); - line = WebPaintTarget.escapeXML(line); - boolean isEmpty = (line.length() == 0 || line.equals("\r")); - - // Code beautification : Comment lines - line = xmlHighlight(line); - - String head = ""; - String tail = ""; - - if (exp != null) { - errornro++; - head = "
      " - + "XSL
      " - + "
      " - + "" - + "
      " - + exp.getLocalizedMessage() + "
      "; - tail = "
      " - + (errornro > 1 ? "Previous error " : "") - + "Next error" + "
      \n"; - } - - if (!(isEmpty && lastLineWasEmpty)) - sb - .append(" 4 ? " bgcolor=\"#eeeeff\"" - : "") - + "> " - + String.valueOf(row) + " " - + head + "" + line + "" + tail - + "\n"); - - lastLineWasEmpty = isEmpty; - - } - - sb.append("\n"); - - return sb.toString(); - } - - /** - * Gets the formated error report on UIDL. - * - * @return the formatted error report. - */ - public String getUIDLErrorReport() { - - String uidl = "UIDL Source Not Available."; - if (paintTarget != null) - uidl = paintTarget.getUIDL(); - StringBuffer sb = new StringBuffer(); - - // Prints the formatted UIDL with errors embedded - int row = 0; - int prev = 0; - int index = 0; - boolean lastLineWasEmpty = false; - - // Appends the error report - sb.append(toString()); - - // Appends UIDL - sb - .append("" - + "\n"); - - while ((index = uidl.indexOf('\n', prev)) >= 0) { - String line = uidl.substring(prev, index); - prev = index + 1; - row++; - - line = WebPaintTarget.escapeXML(line); - boolean isEmpty = (line.length() == 0 || line.equals("\r")); - - // Highlight source - // line = xmlHighlight(line); - - if (!(isEmpty && lastLineWasEmpty)) - sb - .append(" 4 ? " bgcolor=\"#eeeeff\"" - : "") - + ">\n"); - - lastLineWasEmpty = isEmpty; - } - - sb.append("
      " - + "UIDL
      " - + "
       " - + String.valueOf(row) + " " - + "" + line + "" - + "
      \n"); - - return sb.toString(); - } - - /** - * Highlights the XML source. - * - * @param xmlSnippet - * @return - */ - private String xmlHighlight(String xmlSnippet) { - String res = xmlSnippet; - - // Code beautification : Comment lines - DebugWindow.replaceAll(res, "<!--", - "<!--"); - res = DebugWindow.replaceAll(res, "-->", "-->"); - - // nbsp instead of blanks - String l = " "; - while (res.startsWith(" ")) { - l += " "; - res = res.substring(1, res.length()); - } - res = l + res; - - return res; - } - - /** - * Gets the first fatal error. - * - * @return the fatal error. - */ - public Throwable getFirstFatalError() { - return (Throwable) fatals.iterator().next(); - } - - /** - * @see org.xml.sax.ErrorHandler#error(SAXParseException) - */ - public void error(SAXParseException exception) throws SAXException { - errors.addLast(exception); - rowToErrorMap - .put(new Integer(exception.getLineNumber()), exception); - errorToRowMap - .put(exception, new Integer(exception.getLineNumber())); - } - - /** - * @see org.xml.sax.ErrorHandler#fatalError(SAXParseException) - */ - public void fatalError(SAXParseException exception) throws SAXException { - fatals.addLast(exception); - rowToErrorMap - .put(new Integer(exception.getLineNumber()), exception); - errorToRowMap - .put(exception, new Integer(exception.getLineNumber())); - } - - /** - * @see org.xml.sax.ErrorHandler#warning(SAXParseException) - */ - public void warning(SAXParseException exception) throws SAXException { - warnings.addLast(exception); - rowToErrorMap - .put(new Integer(exception.getLineNumber()), exception); - errorToRowMap - .put(exception, new Integer(exception.getLineNumber())); - } - - } - -} diff --git a/src/com/itmill/toolkit/terminal/web/UIDLTransformerException.java b/src/com/itmill/toolkit/terminal/web/UIDLTransformerException.java deleted file mode 100644 index 2aa9c8b535..0000000000 --- a/src/com/itmill/toolkit/terminal/web/UIDLTransformerException.java +++ /dev/null @@ -1,89 +0,0 @@ -/* ************************************************************************* - - 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.web; - -/** - * Exception in the transform process. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ -public class UIDLTransformerException extends java.lang.Exception { - - private static final long serialVersionUID = 5648982356058143223L; - - private String HTMLDescription = null; - - private Throwable transformException = null; - - /** - * Creates a new instance of UIDLTransformerException without detail - * message. - */ - public UIDLTransformerException() { - } - - /** - * Constructs an instance of UIDLTransformerException with the specified - * detail message. - * - * @param msg - * the description of exception that occurred. - * @param te - * the Transform exception that occurred. - * @param desc - * the detailed description. - */ - public UIDLTransformerException(String msg, Throwable te, String desc) { - super(msg); - this.transformException = te; - this.HTMLDescription = desc; - } - - /** - * Returns the detailed description. - * - * @return the Detailed description of exception. - */ - public String getHTMLDescription() { - return HTMLDescription; - } - - /** - * Returns the nested transform exception that occurred. - * - * @return the transform exception - */ - public Throwable getTransformException() { - return transformException; - } - -} diff --git a/src/com/itmill/toolkit/terminal/web/UIDLTransformerFactory.java b/src/com/itmill/toolkit/terminal/web/UIDLTransformerFactory.java deleted file mode 100644 index abaecd7f8a..0000000000 --- a/src/com/itmill/toolkit/terminal/web/UIDLTransformerFactory.java +++ /dev/null @@ -1,347 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.Map; -import java.util.Iterator; - -/** - * Class implementing the UIDLTransformer Factory. The factory creates and - * maintains a pool of transformers that are used for transforming UIDL to HTML. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - -public class UIDLTransformerFactory { - - /** - * Time between repository modified queries. - */ - private static final int CACHE_CHECK_INTERVAL_MILLIS = 5 * 1000; - - /** - * The time transformers are cached by default. - */ - private static final long DEFAULT_TRANSFORMER_CACHETIME = 60 * 60 * 1000; - - /** - * Maximum number of transformers in use. - */ - private int maxConcurrentTransformers = 1; - - /** - * Last time theme modification time was checked. - */ - private long lastModificationCheckTime = 0; - - /** - * Last time theme source was modified. - */ - private long themeSourceModificationTime = 0; - - /** - * How long to cache transformers. - */ - private long cacheTime = DEFAULT_TRANSFORMER_CACHETIME; - - /** - * Spool manager thread. - */ - private SpoolManager spoolManager; - - private Map transformerSpool = new HashMap(); - - private ThemeSource themeSource; - - private ApplicationServlet webAdapterServlet; - - private int transformerCount = 0; - - private int transformersInUse = 0; - - /** - * Constructor for transformer factory. Method UIDLTransformerFactory. - * - * @param themeSource - * the Theme source to be used for themes. - * @param webAdapterServlet - * the Adapter servlet. - * @param maxConcurrentTransformers - * the Maximum number of concurrent themes in use. - * @param cacheTime - * the Time to cache the transformers. - */ - public UIDLTransformerFactory(ThemeSource themeSource, - ApplicationServlet webAdapterServlet, - int maxConcurrentTransformers, long cacheTime) { - this.webAdapterServlet = webAdapterServlet; - if (themeSource == null) - throw new NullPointerException(); - this.themeSource = themeSource; - this.themeSourceModificationTime = themeSource.getModificationTime(); - this.maxConcurrentTransformers = maxConcurrentTransformers; - if (cacheTime >= 0) - this.cacheTime = cacheTime; - this.spoolManager = new SpoolManager(this.cacheTime); - this.spoolManager.setDaemon(true); - // Enable manager only if time > 0 - if (this.cacheTime > 0) - this.spoolManager.start(); - } - - /** - * Gets the new transformer of the specified type. - * - * @param type - * the Type of the requested transformer. - * @return Created new transformer. - * @throws UIDLTransformerException - * if the transform can not be created. - */ - public synchronized UIDLTransformer getTransformer(UIDLTransformerType type) - throws UIDLTransformerException { - - while (transformersInUse >= maxConcurrentTransformers) { - try { - this.wait(); - } catch (InterruptedException e) { - return null; - } - } - - // Gets the list of transformers for this type - TransformerList list = (TransformerList) this.transformerSpool - .get(type); - - // Checks the modification time between fixed intervals - long now = System.currentTimeMillis(); - if (now - CACHE_CHECK_INTERVAL_MILLIS > this.lastModificationCheckTime) { - - this.lastModificationCheckTime = now; - - // Checks if the theme source has been modified and flush - // list if necessary - long lastmod = this.themeSource.getModificationTime(); - if (list != null && this.themeSourceModificationTime < lastmod) { - if (webAdapterServlet.isDebugMode(null)) { - Log.info("Theme source modified since " - + new Date(this.themeSourceModificationTime) - .toString() + ". Reloading..."); - } - // Force refresh by removing from spool - this.transformerSpool.clear(); - list = null; - this.transformerCount = 0; - this.themeSourceModificationTime = lastmod; - } - } - - UIDLTransformer t = null; - - if (list != null && !list.isEmpty()) { - // If available, return the first available transformer - t = (UIDLTransformer) list.removeFirst(); - if (webAdapterServlet.isDebugMode(null)) { - Log.info("Reserved existing transformer: " + type); - } - } else { - - // Creates the new transformer and return it. Transformers are added - // to - // spool when they are released. - t = new UIDLTransformer(type, themeSource, webAdapterServlet); - transformerCount++; - if (webAdapterServlet.isDebugMode(null)) { - Log.info("Created new transformer (" + transformerCount + "):" - + type); - } - - // Creates the new list, if not found - if (list == null) { - list = new TransformerList(); - this.transformerSpool.put(type, list); - if (webAdapterServlet.isDebugMode(null)) { - Log.info("Created new type: " + type); - } - } - - } - transformersInUse++; - return t; - } - - /** - * Recycle a used transformer back to spool. One must guarantee not to use - * the transformer after it have been released. - * - * @param transformer - * the UIDLTransformer to be recycled. - */ - public synchronized void releaseTransformer(UIDLTransformer transformer) { - - try { - // Resets the transformer before returning it to spool - transformer.reset(); - - // Recycle the transformer back to spool - TransformerList list = (TransformerList) this.transformerSpool - .get(transformer.getTransformerType()); - if (list != null) { - list.add(transformer); - if (webAdapterServlet.isDebugMode(null)) { - Log.info("Released transformer: " - + transformer.getTransformerType() + "(In use: " - + transformersInUse + ",Spooled: " + list.size() - + ")"); - } - list.lastUsed = System.currentTimeMillis(); - } else { - Log.info("Tried to release non-existing transformer. Ignoring." - + " (Type:" + transformer.getTransformerType() + ")"); - } - } finally { - if (transformersInUse > 0) - transformersInUse--; - notifyAll(); - } - } - - /** - * - * - * - */ - private class TransformerList { - - private LinkedList list = new LinkedList(); - - private long lastUsed = 0; - - /** - * - * @param transformer - */ - public void add(UIDLTransformer transformer) { - list.add(transformer); - } - - /** - * - * @return - */ - public UIDLTransformer removeFirst() { - return (UIDLTransformer) ((LinkedList) list).removeFirst(); - } - - /** - * - * @return - */ - public boolean isEmpty() { - return list.isEmpty(); - } - - /** - * - * @return - */ - public int size() { - return list.size(); - } - } - - /** - * - * - */ - private synchronized void removeUnusedTransformers() { - long currentTime = System.currentTimeMillis(); - HashSet keys = new HashSet(); - keys.addAll(this.transformerSpool.keySet()); - for (Iterator i = keys.iterator(); i.hasNext();) { - UIDLTransformerType type = (UIDLTransformerType) i.next(); - TransformerList l = (TransformerList) this.transformerSpool - .get(type); - if (l != null) { - if (l.lastUsed > 0 - && l.lastUsed < (currentTime - this.cacheTime)) { - if (webAdapterServlet.isDebugMode(null)) { - Log.info("Removed transformer: " + type - + " Not used since " + new Date(l.lastUsed)); - } - this.transformerSpool.remove(type); - } - } - } - } - - /** - * Class for periodically remove unused transformers from memory. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - protected class SpoolManager extends Thread { - - long refreshTime; - - /** - * - * @param refreshTime - */ - public SpoolManager(long refreshTime) { - super("UIDLTransformerFactory.SpoolManager"); - this.refreshTime = refreshTime; - } - - /** - * - * @see java.lang.Thread#run() - */ - public void run() { - while (true) { - try { - sleep(refreshTime); - } catch (Exception e) { - } - removeUnusedTransformers(); - } - } - } - -} diff --git a/src/com/itmill/toolkit/terminal/web/UIDLTransformerType.java b/src/com/itmill/toolkit/terminal/web/UIDLTransformerType.java deleted file mode 100644 index 33ef3762a4..0000000000 --- a/src/com/itmill/toolkit/terminal/web/UIDLTransformerType.java +++ /dev/null @@ -1,120 +0,0 @@ -/* ************************************************************************* - - 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.web; - -/** - * Type of the transformer. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ -public class UIDLTransformerType { - - /** - * Holds the value of property webBrowserType. - */ - private WebBrowser webBrowser; - - /** - * Holds the value of property theme. - */ - private Theme theme; - - /** - * Creates a new instance of TransformerType. - * - * @param webBrowserType - * the web browser type. - * @param theme - * the property theme. - */ - public UIDLTransformerType(WebBrowser webBrowserType, Theme theme) { - if (webBrowserType == null || theme == null) - throw new IllegalArgumentException( - "WebBrowserType and Theme must be non-null values"); - this.webBrowser = webBrowserType; - this.theme = theme; - } - - /** - * Returns the hash code for this string. - * - * @return the hash code value. - */ - public int hashCode() { - - return this.toString().hashCode(); - } - - /** - * Gets the web browser type used in the UIDLTransformer of this type. - * - * @return the Web browser type used. - */ - public WebBrowser getWebBrowser() { - return this.webBrowser; - } - - /** - * Gets the theme used in the UIDLTransformer of this type. - * - * @return the Theme used. - */ - public Theme getTheme() { - return this.theme; - } - - /** - * Two types are equal, if their properties are equal. - * - * @see java.lang.Object#equals(java.lang.Object) - */ - public boolean equals(Object obj) { - // Checks that the object are of the same class - if (!(obj.getClass().equals(this.getClass()))) - return false; - - // Checks that the properties of the types are equal - return this.toString().equals(obj.toString()); - } - - /** - * Textual representation of the UIDLTransformer type. - * - * @see java.lang.Object#toString() - */ - public String toString() { - return " theme='" + theme.getName() + "' js=" - + webBrowser.getJavaScriptVersion() + "' markup='" - + webBrowser.getMarkupVersion() + "'"; - } - -} diff --git a/src/com/itmill/toolkit/terminal/web/WebApplicationContext.java b/src/com/itmill/toolkit/terminal/web/WebApplicationContext.java deleted file mode 100644 index 09a1279995..0000000000 --- a/src/com/itmill/toolkit/terminal/web/WebApplicationContext.java +++ /dev/null @@ -1,261 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import java.io.File; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.WeakHashMap; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; - -import com.itmill.toolkit.Application; -import com.itmill.toolkit.service.ApplicationContext; -import com.itmill.toolkit.ui.Window; - -/** - * Web application context for the IT Mill Toolkit applications. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.1 - */ -public class WebApplicationContext implements ApplicationContext { - - private List listeners; - - private HttpSession session; - - private WeakHashMap formActions = new WeakHashMap(); - - /** - * Creates a new Web Application Context. - * - * @param session - * the HTTP session. - */ - WebApplicationContext(HttpSession session) { - this.session = session; - } - - /** - * Gets the form action for given window. - *

      - * By default, this action is "", which preserves the current url. Commonly - * this is wanted to be set to application.getUrl.toString or - * window.getUrl.toString in order to clean any local links - * or parameters set from the action. - *

      - * - * @param window - * the Window for which the action is queried. - * @return the Action to be set into Form action attribute. - */ - public String getWindowFormAction(Window window) { - String action = (String) formActions.get(window); - return action == null ? "" : action; - } - - /** - * Sets the form action for given window. - *

      - * By default, this action is "", which preserves the current url. Commonly - * this is wanted to be set to application.getUrl.toString or - * window.getUrl.toString in order to clean any local links - * or parameters set from the action. - *

      - * - * @param window - * the Window for which the action is set. - * @param action - * the New action for the window. - */ - public void setWindowFormAction(Window window, String action) { - if (action == null || action == "") - formActions.remove(window); - else - formActions.put(window, action); - } - - /** - * Gets the application context base directory. - * - * @see com.itmill.toolkit.service.ApplicationContext#getBaseDirectory() - */ - public File getBaseDirectory() { - String realPath = ApplicationServlet.getResourcePath(session - .getServletContext(), "/"); - if (realPath == null) - return null; - return new File(realPath); - } - - /** - * Gets the http-session application is running in. - * - * @return HttpSession this application context resides in. - */ - public HttpSession getHttpSession() { - return session; - } - - /** - * Gets the applications in this context. - * - * @see com.itmill.toolkit.service.ApplicationContext#getApplications() - */ - public Collection getApplications() { - LinkedList applications = (LinkedList) session - .getAttribute(ApplicationServlet.SESSION_ATTR_APPS); - - return Collections - .unmodifiableCollection(applications == null ? (new LinkedList()) - : applications); - } - - /** - * Gets the application context for HttpSession. - * - * @param session - * the HTTP session. - * @return the application context for HttpSession. - */ - static public WebApplicationContext getApplicationContext( - HttpSession session) { - return new WebApplicationContext(session); - } - - /** - * Returns true if and only if the argument is not - * null and is a Boolean object that represents the same - * boolean value as this object. - * - * @param obj - * the object to compare with. - * @see java.lang.Object#equals(java.lang.Object) - */ - public boolean equals(Object obj) { - return session.equals(obj); - } - - /** - * Returns the hash code value . - * - * @see java.lang.Object#hashCode() - */ - public int hashCode() { - return session.hashCode(); - } - - /** - * Adds the transaction listener to this context. - * - * @see com.itmill.toolkit.service.ApplicationContext#addTransactionListener(com.itmill.toolkit.service.ApplicationContext.TransactionListener) - */ - public void addTransactionListener(TransactionListener listener) { - if (this.listeners == null) - this.listeners = new LinkedList(); - this.listeners.add(listener); - } - - /** - * Removes the transaction listener from this context. - * - * @see com.itmill.toolkit.service.ApplicationContext#removeTransactionListener(com.itmill.toolkit.service.ApplicationContext.TransactionListener) - */ - public void removeTransactionListener(TransactionListener listener) { - if (this.listeners != null) - this.listeners.remove(listener); - - } - - /** - * Notifies the transaction start. - * - * @param application - * @param request - * the HTTP request. - */ - protected void startTransaction(Application application, - HttpServletRequest request) { - if (this.listeners == null) - return; - for (Iterator i = this.listeners.iterator(); i.hasNext();) { - ((ApplicationContext.TransactionListener) i.next()) - .transactionStart(application, request); - } - } - - /** - * Notifies the transaction end. - * - * @param application - * @param request - * the HTTP request. - */ - protected void endTransaction(Application application, - HttpServletRequest request) { - if (this.listeners == null) - return; - - LinkedList exceptions = null; - for (Iterator i = this.listeners.iterator(); i.hasNext();) - try { - ((ApplicationContext.TransactionListener) i.next()) - .transactionEnd(application, request); - } catch (RuntimeException t) { - if (exceptions == null) - exceptions = new LinkedList(); - exceptions.add(t); - } - - // If any runtime exceptions occurred, throw a combined exception - if (exceptions != null) { - StringBuffer msg = new StringBuffer(); - for (Iterator i = listeners.iterator(); i.hasNext();) { - RuntimeException e = (RuntimeException) i.next(); - if (msg.length() == 0) - msg.append("\n\n--------------------------\n\n"); - msg.append(e.getMessage() + "\n"); - StringWriter trace = new StringWriter(); - e.printStackTrace(new PrintWriter(trace, true)); - msg.append(trace.toString()); - } - throw new RuntimeException(msg.toString()); - } - } - -} diff --git a/src/com/itmill/toolkit/terminal/web/WebBrowser.java b/src/com/itmill/toolkit/terminal/web/WebBrowser.java deleted file mode 100644 index 93515291d2..0000000000 --- a/src/com/itmill/toolkit/terminal/web/WebBrowser.java +++ /dev/null @@ -1,742 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import com.itmill.toolkit.terminal.Terminal; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.Locale; - -/** - * Web browser terminal type. - * - * This class implements web browser properties, which declare the features of - * the web browser. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ -public class WebBrowser implements Terminal { - - private static WebBrowser DEFAULT = new WebBrowser(); - - /** - * Content type. - */ - private String contentType = "text/html; charset=utf-8"; - - /** - * Holds the collection of accepted locales. - */ - private Collection locales = new ArrayList(); - - /** - * Holds value of property browserApplication. - */ - private String browserApplication = null; - - /** - * Should the client side checkking be done. - */ - private boolean performClientCheck = true; - - /** - * Holds value for property isClientSideChecked. - */ - private boolean clientSideChecked = false; - - /** - * Holds value of property javaScriptVersion. - */ - private JavaScriptVersion javaScriptVersion = JAVASCRIPT_UNCHECKED; - - /** - * Holds value of property javaEnabled. - */ - private boolean javaEnabled = false; - - /** - * Holds value of property frameSupport. - */ - private boolean frameSupport = false; - - /** - * Holds value of property markup version. - */ - private MarkupVersion markupVersion = MARKUP_HTML_3_2; - - /** - * Pixel width of the terminal screen. - */ - private int screenWidth = -1; - - /** - * Pixel height of the terminal screen. - */ - private int screenHeight = -1; - - private RenderingMode renderingMode = RENDERING_MODE_UNDEFINED; - - /** - * Constuctor with some autorecognition capabilities Retrieves all - * capability information reported in http request headers: - *
        - *
      • User web browser (User-Agent)
      • - *
      • Supported locale(s)
      • - *
      - */ - - /** - * Constructor WebBrowserType. Creates a default WebBrowserType instance. - */ - public WebBrowser() { - } - - /** - * Gets the name of the default theme. - * - * @return the Name of the terminal window. - */ - public String getDefaultTheme() { - return ApplicationServlet.DEFAULT_THEME; - } - - /** - * Gets the name and version of the web browser application. This is the - * version string reported by the web-browser in http headers. - * - * @return the Web browser application. - */ - public String getBrowserApplication() { - return this.browserApplication; - } - - /** - * Gets the version of the supported Java Script by the browser. - * - * Null if the Java Script is not supported. - * - * @return the Version of the supported Java Script. - */ - public JavaScriptVersion getJavaScriptVersion() { - return this.javaScriptVersion; - } - - /** - * Does the browser support frames ? - * - * @return true if the browser supports frames, otherwise - * false. - */ - public boolean isFrameSupport() { - return this.frameSupport; - } - - /** - * Sets the browser frame support. - * - * @param frameSupport - * True if the browser supports frames, False if not. - */ - public void setFrameSupport(boolean frameSupport) { - this.frameSupport = frameSupport; - } - - /** - * Gets the supported markup language. - * - * @return the Supported markup language - */ - public MarkupVersion getMarkupVersion() { - return this.markupVersion; - } - - /** - * Gets the height of the terminal window in pixels. - * - * @return the Height of the terminal window. - */ - public int getScreenHeight() { - return this.screenHeight; - } - - /** - * Gets the width of the terminal window in pixels. - * - * @return the Width of the terminal window. - */ - public int getScreenWidth() { - return this.screenWidth; - } - - /** - * Gets the default locale requested by the browser. - * - * @return the Default locale. - */ - public Locale getDefaultLocale() { - if (this.locales.isEmpty()) - return null; - return (Locale) this.locales.iterator().next(); - } - - /** - * Hash code composed of the properties of the web browser type. - * - * @see java.lang.Object#hashCode() - */ - public int hashCode() { - return toString().hashCode(); - } - - /** - * Tests the equality of the properties for two web browser types. - * - * @see java.lang.Object#equals(java.lang.Object) - */ - public boolean equals(Object obj) { - if (obj != null && obj instanceof WebBrowser) { - return toString().equals(obj.toString()); - } - return false; - } - - /** - * @see java.lang.Object#toString() - */ - public String toString() { - - String localeString = "["; - for (Iterator i = this.locales.iterator(); i.hasNext(); localeString += ",") { - localeString += ((Locale) i.next()).toString(); - } - localeString += "]"; - - // Returns catenation of the properties - return "Browser:" + this.browserApplication + ", " + "Locales:" - + localeString + ", " + "Frames:" + this.frameSupport + ", " - + "JavaScript:" + this.javaScriptVersion + ", " + "Java: " - + this.javaEnabled + ", " + "Markup:" + this.markupVersion - + ", " + "Height:" + this.screenHeight + ", " + "Width:" - + this.screenWidth + ", ClientCheck:" + this.performClientCheck - + ", ClientCheckDone:" + this.clientSideChecked; - } - - /** - * Gets the preferred content type. - * - * @return the content type. - */ - public String getContentType() { - return contentType; - } - - /** - * Checks if this type supports also given browser. - * - * @param browser - * the browser type. - * @return true if this type matches the given browser. - */ - public boolean supports(String browser) { - return this.getBrowserApplication().indexOf(browser) >= 0; - } - - /** - * Checks if this type supports given markup language version. - * - * @param html - * the markup language version. - * @return true if this type supports the given markup version,otherwise false. - */ - public boolean supports(MarkupVersion html) { - return this.getMarkupVersion().supports(html); - } - - /** - * Checks if this type supports given javascript version. - * - * @param js - * the javascript version to check for. - * @return true if this type supports the given javascript version. - */ - public boolean supports(JavaScriptVersion js) { - return this.getJavaScriptVersion().supports(js); - } - - /** - * Parses HTML version from string. - * - * @param html - * @return HTMLVersion instance. - */ - private MarkupVersion doParseHTMLVersion(String html) { - for (int i = 0; i < MARKUP_VERSIONS.length; i++) { - if (MARKUP_VERSIONS[i].name.equals(html)) - return MARKUP_VERSIONS[i]; - } - return MARKUP_UNKNOWN; - } - - /** - * Parses JavaScript version from string. - * - * @param js - * the javascript version to check for. - * @return HTMLVersion instance. - */ - private JavaScriptVersion doParseJavaScriptVersion(String js) { - for (int i = 0; i < JAVASCRIPT_VERSIONS.length; i++) { - if (JAVASCRIPT_VERSIONS[i].name.toLowerCase().startsWith( - js.toLowerCase())) - return JAVASCRIPT_VERSIONS[i]; - } - return JAVASCRIPT_NONE; - } - - /** - * Parses HTML version from string. - * - * @param html - * @return the HTMLVersion instance. - */ - public static MarkupVersion parseHTMLVersion(String html) { - return DEFAULT.doParseHTMLVersion(html); - } - - /** - * Parse JavaScript version from string. - * - * @param js - * the javascript version to check for. - * @return the HTMLVersion instance. - */ - public static JavaScriptVersion parseJavaScriptVersion(String js) { - return DEFAULT.doParseJavaScriptVersion(js); - } - - /** - * Gets the client side cheked property. Certain terminal features can only - * be detected at client side. This property indicates if the client side - * detections have been performed for this type. - * - * @return true if client has sent information about its - * properties. Default is false. - */ - public boolean isClientSideChecked() { - return this.clientSideChecked; - } - - /** - * Sets the client side checked property. Certain terminal features can only - * be detected at client side. This property indicates if the client side - * detections have been performed for this type. - * - * @param value - * true if client has sent information about its properties, - * false otherweise. - */ - public void setClientSideChecked(boolean value) { - this.clientSideChecked = value; - } - - /** - * Should the client features be checked using remote scripts. Should the - * client side terminal feature check be performed. - * - * @return true if client side checking should be performed - * for this terminal type. Default is false. - */ - public boolean performClientCheck() { - return this.performClientCheck; - } - - /** - * Should the client features be checked using remote scripts. - * - * @param value - * @return true if client side checking should be performed - * for this terminal type. Default false. - */ - public void performClientCheck(boolean value) { - this.performClientCheck = value; - } - - /** - * Checks if web browser supports Java. - * - * @return true if the browser supports java otherwise false. - */ - public boolean isJavaEnabled() { - return javaEnabled; - } - - /** - * Returns the locales supported by the web browser. - * - * @return the Collection. - */ - public Collection getLocales() { - return locales; - } - - /** - * Sets the browser application. This corresponds to User-Agent HTTP header. - * - * @param browserApplication - * the browserApplication to set. - */ - public void setBrowserApplication(String browserApplication) { - this.browserApplication = browserApplication; - } - - /** - * Sets the default content type. Default is text/html - * - * @param contentType - * the contentType to set. - */ - public void setContentType(String contentType) { - this.contentType = contentType; - } - - /** - * Sets the java enabled property. - * - * @param javaEnabled - * the javaEnabled to set. - */ - public void setJavaEnabled(boolean javaEnabled) { - this.javaEnabled = javaEnabled; - } - - /** - * Sets the JavaScript version. - * - * @param javaScriptVersion - * the JavaScript version to set. - */ - public void setJavaScriptVersion(JavaScriptVersion javaScriptVersion) { - this.javaScriptVersion = javaScriptVersion; - } - - /** - * Sets the markup language version. - * - * @param markupVersion - * the markup language version to set. - */ - public void setMarkupVersion(MarkupVersion markupVersion) { - this.markupVersion = markupVersion; - } - - /** - * Sets the screen height. - * - * @param screenHeight - * the screen height to set in pixels. - */ - public void setScreenHeight(int screenHeight) { - this.screenHeight = screenHeight; - } - - /** - * Sets the screen width. - * - * @param screenWidth - * the screenWidth to set in pixels. - */ - public void setScreenWidth(int screenWidth) { - this.screenWidth = screenWidth; - } - - /* - * Consts defining the supported markup language versions @author IT Mill - * Ltd. - * - * @version @VERSION@ - * @since 3.0 - */ - public class MarkupVersion { - private String name; - - private int order; - - /** - * Returns true if and only if the argument is not - * null and is a Boolean object that represents the same - * boolean value as this object. - * - * @param obj - * the object to compare with. - * @see java.lang.Object#equals(Object) - */ - public boolean equals(Object obj) { - if (obj != null && obj instanceof MarkupVersion) - return name.equals(((MarkupVersion) obj).name); - return false; - } - - /** - * @see java.lang.Object#toString() - */ - public String toString() { - return name; - } - - /** - * - * @param name - * @param order - */ - private MarkupVersion(String name, int order) { - this.name = name; - this.order = order; - } - - /** - * Checks the compability with other HTML version. - * - * @param other - * the HTML version. - * @return true if this is compatible with the other, - * otherwise false. - */ - public boolean supports(MarkupVersion other) { - return (this.order >= other.order); - } - - } - - public static final MarkupVersion MARKUP_UNKNOWN = DEFAULT.new MarkupVersion( - "HTML unknown", 0); - - public static final MarkupVersion MARKUP_HTML_2_0 = DEFAULT.new MarkupVersion( - "HTML 2.0", 20); - - public static final MarkupVersion MARKUP_HTML_3_2 = DEFAULT.new MarkupVersion( - "HTML 3.2", 32); - - public static final MarkupVersion MARKUP_HTML_4_0 = DEFAULT.new MarkupVersion( - "HTML 4.0", 40); - - public static final MarkupVersion MARKUP_XHTML_1_0 = DEFAULT.new MarkupVersion( - "XHTML 1.0", 110); - - public static final MarkupVersion MARKUP_XHTML_2_0 = DEFAULT.new MarkupVersion( - "XHTML 2.0", 120); - - public static final MarkupVersion MARKUP_WML_1_0 = DEFAULT.new MarkupVersion( - "WML 1.0", 10); - - public static final MarkupVersion MARKUP_WML_1_1 = DEFAULT.new MarkupVersion( - "WML 1.1", 11); - - public static final MarkupVersion MARKUP_WML_1_2 = DEFAULT.new MarkupVersion( - "WML 1.2", 12); - - public static final MarkupVersion[] MARKUP_VERSIONS = new MarkupVersion[] { - MARKUP_UNKNOWN, MARKUP_HTML_2_0, MARKUP_HTML_3_2, MARKUP_HTML_4_0, - MARKUP_XHTML_1_0, MARKUP_XHTML_2_0, MARKUP_WML_1_0, MARKUP_WML_1_1, - MARKUP_WML_1_2 }; - - /* - * Consts defining the supported JavaScript versions @author IT Mill Ltd. - * - * @version @VERSION@ - * @since 3.0 - */ - public class JavaScriptVersion { - private String name; - - private int order; - - /** - * @see java.lang.Object#equals(Object) - */ - public boolean equals(Object obj) { - if (obj != null && obj instanceof JavaScriptVersion) - return name.equals(((JavaScriptVersion) obj).name); - return false; - } - - /** - * @see java.lang.Object#toString() - */ - public String toString() { - return name; - } - - /** - * - * @param name - * @param order - */ - private JavaScriptVersion(String name, int order) { - this.name = name; - this.order = order; - } - - /** - * Checks the compability with other JavaScript version. Use this like: - * boolean isEcma = someVersion.supports(ECMA_262); - * - * @param other - * the java script version. - * @return true if this supports the other, otherwise - * false. - */ - public boolean supports(JavaScriptVersion other) { - - // ECMA-262 support compare - if (other.equals(ECMA_262)) { - - // JScript over 5.0 support ECMA-262 - if (this.order >= 100) { - return (this.order >= JSCRIPT_5_0.order); - } else { - return (this.order >= JAVASCRIPT_1_3.order); - } - } - - // JavaScript version compare - else if (this.order < 100 && other.order < 100) { - return (this.order >= other.order); - } - - // JScript version compare - else if (this.order >= 100 && other.order >= 100) { - return (this.order >= other.order); - } - - return false; - - } - - } - - public static final JavaScriptVersion JAVASCRIPT_UNCHECKED = DEFAULT.new JavaScriptVersion( - "JavaScript unchecked", -1); - - public static final JavaScriptVersion JAVASCRIPT_NONE = DEFAULT.new JavaScriptVersion( - "JavaScript none", -1); - - public static final JavaScriptVersion JAVASCRIPT_1_0 = DEFAULT.new JavaScriptVersion( - "JavaScript 1.0", 10); - - public static final JavaScriptVersion JAVASCRIPT_1_1 = DEFAULT.new JavaScriptVersion( - "JavaScript 1.1", 11); - - public static final JavaScriptVersion JAVASCRIPT_1_2 = DEFAULT.new JavaScriptVersion( - "JavaScript 1.2", 12); - - public static final JavaScriptVersion JAVASCRIPT_1_3 = DEFAULT.new JavaScriptVersion( - "JavaScript 1.3", 13); - - public static final JavaScriptVersion JAVASCRIPT_1_4 = DEFAULT.new JavaScriptVersion( - "JavaScript 1.4", 14); - - public static final JavaScriptVersion JAVASCRIPT_1_5 = DEFAULT.new JavaScriptVersion( - "JavaScript 1.5", 15); - - public static final JavaScriptVersion JSCRIPT_1_0 = DEFAULT.new JavaScriptVersion( - "JScript 1.0", 110); - - public static final JavaScriptVersion JSCRIPT_3_0 = DEFAULT.new JavaScriptVersion( - "JScript 3.0", 130); - - public static final JavaScriptVersion JSCRIPT_4_0 = DEFAULT.new JavaScriptVersion( - "JScript 4.0", 140); - - public static final JavaScriptVersion JSCRIPT_5_0 = DEFAULT.new JavaScriptVersion( - "JScript 5.0", 150); - - public static final JavaScriptVersion JSCRIPT_5_1 = DEFAULT.new JavaScriptVersion( - "JScript 5.1", 151); - - public static final JavaScriptVersion JSCRIPT_5_5 = DEFAULT.new JavaScriptVersion( - "JScript 5.5", 155); - - public static final JavaScriptVersion JSCRIPT_5_6 = DEFAULT.new JavaScriptVersion( - "JScript 5.6", 156); - - public static final JavaScriptVersion JSCRIPT_5_7 = DEFAULT.new JavaScriptVersion( - "JScript 5.7", 157); - - public static final JavaScriptVersion ECMA_262 = DEFAULT.new JavaScriptVersion( - "ECMA-262", 262); - - public static final JavaScriptVersion[] JAVASCRIPT_VERSIONS = new JavaScriptVersion[] { - JAVASCRIPT_UNCHECKED, JAVASCRIPT_NONE, JAVASCRIPT_1_0, - JAVASCRIPT_1_1, JAVASCRIPT_1_2, JAVASCRIPT_1_3, JAVASCRIPT_1_4, - JAVASCRIPT_1_5, JSCRIPT_1_0, JSCRIPT_3_0, JSCRIPT_4_0, JSCRIPT_5_0, - JSCRIPT_5_1, JSCRIPT_5_5, JSCRIPT_5_6, JSCRIPT_5_7, ECMA_262 }; - - /* - * Consts defining the rendering mode @author IT Mill Ltd. - * - * @version @VERSION@ - * @since 4.0 - */ - public class RenderingMode { - RenderingMode() { - - } - } - - public static final RenderingMode RENDERING_MODE_UNDEFINED = DEFAULT.new RenderingMode(); - - public static final RenderingMode RENDERING_MODE_HTML = DEFAULT.new RenderingMode(); - - public static final RenderingMode RENDERING_MODE_AJAX = DEFAULT.new RenderingMode(); - - /** - * Gets the current rendering mode. - * - * @return the current rendering mode. - */ - public RenderingMode getRenderingMode() { - return renderingMode; - } - - /** - * Sets the current rendering mode. - * - * @param renderingMode - * the rendering mode. - */ - public void setRenderingMode(RenderingMode renderingMode) { - this.renderingMode = renderingMode; - } - -} diff --git a/src/com/itmill/toolkit/terminal/web/WebBrowserProbe.java b/src/com/itmill/toolkit/terminal/web/WebBrowserProbe.java deleted file mode 100644 index 76bcd1f986..0000000000 --- a/src/com/itmill/toolkit/terminal/web/WebBrowserProbe.java +++ /dev/null @@ -1,349 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import java.util.Collection; -import java.util.Enumeration; -import java.util.Map; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; - -/** - * The WebBrowserProbe uses JavaScript to determine the - * capabilities of the client browser. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ -public class WebBrowserProbe { - - private static final String WA_NOSCRIPT = "WA_NOSCRIPT"; - - private static final String CLIENT_TYPE = "wa_browser"; - - /** - * Returns the terminal type from the given session. - * - * @param session - * the HTTP session. - * @return WebBrowser instance for the given session. - */ - public static WebBrowser getTerminalType(HttpSession session) { - if (session != null) - return (WebBrowser) session.getAttribute(CLIENT_TYPE); - return null; - } - - /** - * Sets the terminal type for the given session. - * - * @param session - * the HTTP session. - * @param terminal - * the web browser. - * @return WebBrowser instance for the given session. - */ - public static void setTerminalType(HttpSession session, WebBrowser terminal) { - if (session != null) - session.setAttribute(CLIENT_TYPE, terminal); - } - - /** - * Handles the client checking. - * - * @param request - * the HTTP request to process. - * @param parameters - * the Parameters to be used as defaults. - * @return true if response should include a probe - * script,otherwise false. - * @throws ServletException - * if an exception has occurred that interferes with the - * servlet's normal operation. - */ - public static boolean handleProbeRequest(HttpServletRequest request, - Map parameters) throws ServletException { - - HttpSession s = request.getSession(); - WebBrowser browser = getTerminalType(s); - if (browser != null) { - - // Check if no-script was requested - if (parameters.containsKey(WA_NOSCRIPT)) { - String val = ((String[]) parameters.get(WA_NOSCRIPT))[0]; - if (val != null && "1".equals(val)) { - browser.setJavaScriptVersion(WebBrowser.JAVASCRIPT_NONE); - browser.setClientSideChecked(true); - } else { - // Recheck - browser.setClientSideChecked(false); - } - } - - // If client is alredy checked disable further checking - if (browser.isClientSideChecked()) - return false; - - } - - // Creates new type based on client parameters - browser = probe(browser, request, parameters); - setTerminalType(s, browser); - - // Sets client as checked if parameters were found - if (parameters.containsKey("wa_clientprobe")) { - String val = ((String[]) parameters.get("wa_clientprobe"))[0]; - browser.setClientSideChecked(val != null && "1".equals(val)); - } - - // Include probe script if requested and not alredy probed - return browser.performClientCheck() && !browser.isClientSideChecked(); - - } - - /** - * Determines versions based on user agent string. - * - * @param agent - * the HTTP User-Agent request header. - * @return new WebBrowser instance initialized based on agent features. - */ - public static WebBrowser probe(String agent) { - WebBrowser res = new WebBrowser(); - if (agent == null) - return res; - - // Set the agent string - res.setBrowserApplication(agent); - - // Konqueror - if (agent.indexOf("Konqueror") >= 0) { - res.setJavaScriptVersion(WebBrowser.JSCRIPT_5_6); - res.setJavaEnabled(true); - res.setFrameSupport(true); - } - - // Opera - else if (agent.indexOf("Opera 3.") >= 0) { - res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_1_3); - res.setJavaEnabled(false); - res.setFrameSupport(true); - } else if (agent.indexOf("Opera") >= 0) { - res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_1_3); - res.setJavaEnabled(true); - res.setFrameSupport(true); - res.setMarkupVersion(WebBrowser.MARKUP_HTML_4_0); - if (agent.indexOf("Opera/9") >= 0) - res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_1_5); - } - - // OmniWeb - else if (agent.indexOf("OmniWeb") >= 0) { - res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_1_3); - res.setJavaEnabled(true); - res.setFrameSupport(true); - } - - // Mosaic - else if (agent.indexOf("Mosaic") >= 0) { - res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_1_3); - res.setJavaEnabled(true); - res.setFrameSupport(true); - res.setMarkupVersion(WebBrowser.MARKUP_HTML_2_0); - } - - // Lynx - else if (agent.indexOf("Lynx") >= 0) { - res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_NONE); - res.setJavaEnabled(false); - res.setFrameSupport(true); - } - - // Microsoft Browsers - // See Microsoft documentation for details: - // http://msdn.microsoft.com/library/default.asp?url=/library/ - // en-us/script56/html/js56jsoriversioninformation.asp - else if (agent.indexOf("MSIE 7.") >= 0) { - res.setJavaScriptVersion(WebBrowser.JSCRIPT_5_7); - res.setJavaEnabled(true); - res.setFrameSupport(true); - res.setMarkupVersion(WebBrowser.MARKUP_HTML_4_0); - } else if (agent.indexOf("MSIE 6.") >= 0) { - res.setJavaScriptVersion(WebBrowser.JSCRIPT_5_6); - res.setJavaEnabled(true); - res.setFrameSupport(true); - res.setMarkupVersion(WebBrowser.MARKUP_HTML_4_0); - } else if (agent.indexOf("MSIE 5.5") >= 0) { - res.setJavaScriptVersion(WebBrowser.JSCRIPT_5_5); - res.setJavaEnabled(true); - res.setFrameSupport(true); - res.setMarkupVersion(WebBrowser.MARKUP_HTML_4_0); - } else if (agent.indexOf("MSIE 5.") >= 0) { - res.setJavaScriptVersion(WebBrowser.JSCRIPT_5_0); - res.setJavaEnabled(true); - res.setFrameSupport(true); - res.setMarkupVersion(WebBrowser.MARKUP_HTML_4_0); - } else if (agent.indexOf("MSIE 4.") >= 0) { - res.setJavaScriptVersion(WebBrowser.JSCRIPT_3_0); - res.setJavaEnabled(true); - res.setFrameSupport(true); - res.setMarkupVersion(WebBrowser.MARKUP_HTML_4_0); - } else if (agent.indexOf("MSIE 3.") >= 0) { - res.setJavaScriptVersion(WebBrowser.JSCRIPT_3_0); - res.setJavaEnabled(true); - res.setFrameSupport(true); - } else if (agent.indexOf("MSIE 2.") >= 0) { - res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_NONE); - res.setJavaEnabled(false); - if (agent.indexOf("Mac") >= 0) { - res.setFrameSupport(true); - } else { - res.setFrameSupport(false); - } - } - - // Netscape browsers - else if (agent.indexOf("Netscape6") >= 0) { - res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_1_5); - res.setJavaEnabled(true); - res.setFrameSupport(true); - res.setMarkupVersion(WebBrowser.MARKUP_HTML_4_0); - } else if ((agent.indexOf("Mozilla/4.06") >= 0) - || (agent.indexOf("Mozilla/4.7") >= 0)) { - res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_1_3); - res.setJavaEnabled(true); - res.setFrameSupport(true); - res.setMarkupVersion(WebBrowser.MARKUP_HTML_4_0); - } else if (agent.indexOf("Mozilla/4.") >= 0) { - res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_1_2); - res.setJavaEnabled(true); - res.setFrameSupport(true); - } else if (agent.indexOf("Mozilla/3.") >= 0) { - res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_1_1); - res.setJavaEnabled(true); - res.setFrameSupport(true); - } else if (agent.indexOf("Mozilla/2.") >= 0) { - res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_1_0); - res.setJavaEnabled(true); - res.setFrameSupport(true); - } - - // Mozilla Open-Source Browsers - else if (agent.indexOf("Mozilla/5.") >= 0) { - res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_1_5); - res.setJavaEnabled(true); - res.setFrameSupport(true); - res.setMarkupVersion(WebBrowser.MARKUP_HTML_4_0); - } - - // Unknown browser - else { - res.setJavaScriptVersion(WebBrowser.JAVASCRIPT_UNCHECKED); - res.setJavaEnabled(false); - res.setMarkupVersion(WebBrowser.MARKUP_UNKNOWN); - res.setFrameSupport(false); - } - - return res; - } - - /** - * Creates new instance of WebBrowser by initializing the values based on - * user request. - * - * @param browser - * the browser to be updated. If null a new instance is created. - * @param request - * the Request to be used as defaults. - * @param params - * the Parameters to be used as defaults. - * @return new WebBrowser instance initialized based on request parameters. - */ - public static WebBrowser probe(WebBrowser browser, - HttpServletRequest request, Map params) { - - // Initialize defaults based on client features - WebBrowser res = browser; - if (res == null) { - res = probe(request.getHeader("User-Agent")); - } - - // Client locales - Collection locales = res.getLocales(); - locales.clear(); - for (Enumeration e = request.getLocales(); e.hasMoreElements();) { - locales.add(e.nextElement()); - } - - // Javascript version - if (params.containsKey("wa_jsversion")) { - String val = ((String[]) params.get("wa_jsversion"))[0]; - if (val != null) { - res - .setJavaScriptVersion(WebBrowser - .parseJavaScriptVersion(val)); - } - } - // Java support - if (params.containsKey("wa_javaenabled")) { - String val = ((String[]) params.get("wa_javaenabled"))[0]; - if (val != null) { - res.setJavaEnabled(Boolean.valueOf(val).booleanValue()); - } - } - // Screen width - if (params.containsKey("wa_screenwidth")) { - String val = ((String[]) params.get("wa_screenwidth"))[0]; - if (val != null) { - try { - res.setScreenWidth(Integer.parseInt(val)); - } catch (NumberFormatException e) { - res.setScreenWidth(-1); - } - } - } - // Screen height - if (params.containsKey("wa_screenheight")) { - String val = ((String[]) params.get("wa_screenheight"))[0]; - if (val != null) { - try { - res.setScreenHeight(Integer.parseInt(val)); - } catch (NumberFormatException e) { - res.setScreenHeight(-1); - } - } - } - - return res; - } -} \ No newline at end of file diff --git a/src/com/itmill/toolkit/terminal/web/WebPaintTarget.java b/src/com/itmill/toolkit/terminal/web/WebPaintTarget.java deleted file mode 100644 index 3b8564a703..0000000000 --- a/src/com/itmill/toolkit/terminal/web/WebPaintTarget.java +++ /dev/null @@ -1,734 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import com.itmill.toolkit.Application; -import com.itmill.toolkit.terminal.ApplicationResource; -import com.itmill.toolkit.terminal.ExternalResource; -import com.itmill.toolkit.terminal.PaintException; -import com.itmill.toolkit.terminal.PaintTarget; -import com.itmill.toolkit.terminal.Paintable; -import com.itmill.toolkit.terminal.Resource; -import com.itmill.toolkit.terminal.ThemeResource; -import com.itmill.toolkit.terminal.UploadStream; -import com.itmill.toolkit.terminal.VariableOwner; - -import java.util.Stack; - -/** - * User Interface Description Language Target. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ -public class WebPaintTarget implements PaintTarget { - - /* Document type declarations */ - private final static String UIDL_XML_DECL = ""; - - /* commonly used tags and argument names */ - private final static String UIDL_ARG_NAME = "name"; - - private final static String UIDL_ARG_VALUE = "value"; - - private final static String UIDL_ARG_ID = "id"; - - private Stack mOpenTags; - - private boolean mTagArgumentListOpen; - - private StringBuffer uidlBuffer; - - private StringBuffer tagBuffer; - - private HttpVariableMap variableMap; - - private boolean closed = false; - - private ApplicationServlet webAdapterServlet; - - private Theme theme; - - private static final int TAG_BUFFER_DEFAULT_SIZE = 20; - - private boolean mSuppressOutput = false; - - /** - * Creates a new XMLPrintWriter, without automatic line flushing. - * - * @param out - * A character-output stream. - */ - public WebPaintTarget(HttpVariableMap variableMap, - UIDLTransformerType type, ApplicationServlet webAdapterServlet, - Theme theme) throws PaintException { - - // Host servlet - this.webAdapterServlet = webAdapterServlet; - - // Target theme - this.theme = theme; - - // Sets the variable map - this.variableMap = variableMap; - - // Sets the target for UIDL writing - this.uidlBuffer = new StringBuffer(); - - // Sets the target for TAG data - this.tagBuffer = new StringBuffer(); - - // Initialize tag-writing - mOpenTags = new Stack(); - mTagArgumentListOpen = false; - - // Adds document declaration - this.print(UIDL_XML_DECL + "\n\n"); - - // Adds UIDL start tag and its attributes - this.startTag("uidl"); - - // Name of the active theme - this.addAttribute("theme", type.getTheme().getName()); - - } - - /** - * Ensures that the currently open element tag is closed. - */ - private void ensureClosedTag() { - if (mTagArgumentListOpen) { - tagBuffer.append(">"); - mTagArgumentListOpen = false; - append(tagBuffer); - } - } - - /** - * Prints element start tag. - * - *
      -	 * Todo:
      -	 * Checking of input values
      -	 * 
      - * - * @param tagName - * the name of the start tag. - * @throws PaintException - * if the paint operation failed. - * - */ - public void startTag(String tagName) throws PaintException { - // In case of null data output nothing: - if (tagName == null) - throw new NullPointerException(); - - // Ensure that the target is open - if (this.closed) - throw new PaintException( - "Attempted to write to a closed PaintTarget."); - - // Make sure that the open start tag is closed before - // anything is written. - ensureClosedTag(); - - // Check tagName and attributes here - mOpenTags.push(tagName); - tagBuffer = new StringBuffer(TAG_BUFFER_DEFAULT_SIZE); - - // Print the tag with attributes - tagBuffer.append("<" + tagName); - - mTagArgumentListOpen = true; - } - - /** - * Prints element end tag. - * - * If the parent tag is closed before every child tag is closed a - * PaintException is raised. - * - * @param tagName - * the name of the end tag. - * @throws PaintException - * if the paint operation failed. - */ - public void endTag(String tagName) throws PaintException { - // In case of null data output nothing: - if (tagName == null) - throw new NullPointerException(); - - // Ensures that the target is open - if (this.closed) - throw new PaintException( - "Attempted to write to a closed PaintTarget."); - - String lastTag = ""; - - lastTag = (String) mOpenTags.pop(); - if (!tagName.equalsIgnoreCase(lastTag)) - throw new PaintException("Invalid UIDL: wrong ending tag: '" - + tagName + "' expected: '" + lastTag + "'."); - - // Makes sure that the open start tag is closed before - // anything is written. - ensureClosedTag(); - - // Writes the end (closing) tag - append(""); - - // NOTE: We re-enable the output (if it has been disabled) - // for subsequent tags. The output is suppressed if tag - // contains attribute "invisible" with value true. - mSuppressOutput = false; - } - - /** - * Appends data into UIDL output buffer. - * - * @param data - * the String to be appended. - */ - private void append(String data) { - if (!mSuppressOutput) { - uidlBuffer.append(data); - } - } - - /** - * Appends data into UIDL output buffer. - * - * @param data - * the StringBuffer to be appended. - */ - private void append(StringBuffer data) { - if (!mSuppressOutput) { - uidlBuffer.append(data); - } - } - - /** - * Substitutes the XML sensitive characters with predefined XML entities. - * - * @param xml - * @return A new string instance where all occurrences of XML sensitive - * characters are substituted with entities. - */ - static public String escapeXML(String xml) { - if (xml == null || xml.length() <= 0) - return ""; - return escapeXML(new StringBuffer(xml)).toString(); - } - - /** - * Substitutes the XML sensitive characters with predefined XML entities. - * - * @param xml - * the String to be substituted. - * @return A new StringBuffer instance where all occurrences of XML - * sensitive characters are substituted with entities. - * - */ - static public StringBuffer escapeXML(StringBuffer xml) { - if (xml == null || xml.length() <= 0) - return new StringBuffer(""); - - StringBuffer result = new StringBuffer(xml.length() * 2); - - for (int i = 0; i < xml.length(); i++) { - char c = xml.charAt(i); - String s = toXmlChar(c); - if (s != null) { - result.append(s); - } else { - result.append(c); - } - } - return result; - } - - /** - * Substitutes a XML sensitive character with predefined XML entity. - * - * @param c - * the Character to be replaced with an entity. - * @return String of the entity or null if character is not to be replaced - * with an entity. - */ - private static String toXmlChar(char c) { - switch (c) { - case '&': - return "&"; // & => & - case '>': - return ">"; // > => > - case '<': - return "<"; // < => < - case '"': - return """; // " => " - case '\'': - return "'"; // ' => ' - default: - return null; - } - } - - /** - * Prints XML. - * - * Writes pre-formatted XML to stream. Well-formness of XML is checked. - * - *
      -	 * TODO: XML checking should be made
      -	 * 
      - * - * @param str - */ - private void print(String str) { - // In case of null data output nothing: - if (str == null) - return; - - // Make sure that the open start tag is closed before - // anything is written. - ensureClosedTag(); - - // Write what was given - append(str); - } - - /** - * Prints XML-escaped text. - * - * @param str - * @throws PaintException - * if the paint operation failed. - * - */ - public void addText(String str) throws PaintException { - addUIDL(escapeXML(str)); - } - - /** - * Adds a boolean attribute to component. Atributes must be added before any - * content is written. - * - * @param name - * the Attribute name. - * @param value - * the Attribute value. - * @throws PaintException - * if the paint operation failed. - */ - public void addAttribute(String name, boolean value) throws PaintException { - if ("invisible".equals(name) && value) { - // NOTE: If we receive the "invisible attribute - // we filter these tags (and ceontent) from - // them out from the output. - this.mSuppressOutput = true; - } else { - addAttribute(name, String.valueOf(value)); - } - } - - /** - * Adds a resource attribute to component. Atributes must be added before - * any content is written. - * - * @param name - * the Attribute name. - * @param value - * the Attribute value. - * @throws PaintException - * if the paint operation failed. - */ - public void addAttribute(String name, Resource value) throws PaintException { - - if (value instanceof ExternalResource) { - addAttribute(name, ((ExternalResource) value).getURL()); - - } else if (value instanceof ApplicationResource) { - ApplicationResource r = (ApplicationResource) value; - Application a = r.getApplication(); - if (a == null) - throw new PaintException( - "Application not specified for resorce " - + value.getClass().getName()); - String uri = a.getURL().getPath(); - if (uri.charAt(uri.length() - 1) != '/') - uri += "/"; - uri += a.getRelativeLocation(r); - addAttribute(name, uri); - - } else if (value instanceof ThemeResource) { - addAttribute(name, webAdapterServlet.getResourceLocation(theme - .getName(), (ThemeResource) value)); - } else - throw new PaintException("Web adapter does not " - + "support resources of type: " - + value.getClass().getName()); - - } - - /** - * Adds a integer attribute to component. Atributes must be added before any - * content is written. - * - * @param name - * the Attribute name. - * @param value - * the Attribute value. - * @throws PaintException - * if the paint operation failed. - */ - public void addAttribute(String name, int value) throws PaintException { - addAttribute(name, String.valueOf(value)); - } - - /** - * Adds a long attribute to component. Atributes must be added before any - * content is written. - * - * @param name - * the Attribute name. - * @param value - * the Attribute value. - * @throws PaintException - * if the paint operation failed. - */ - public void addAttribute(String name, long value) throws PaintException { - addAttribute(name, String.valueOf(value)); - } - - /** - * Adds a string attribute to component. Atributes must be added before any - * content is written. - * - * @param name - * the Boolean attribute name. - * @param value - * the Boolean attribute value. - * @throws PaintException - * if the paint operation failed. - */ - public void addAttribute(String name, String value) throws PaintException { - // In case of null data output nothing: - if ((value == null) || (name == null)) - throw new NullPointerException( - "Parameters must be non-null strings (" + name + "=" - + value + ")"); - - // Ensure that the target is open - if (this.closed) - throw new PaintException( - "Attempted to write to a closed PaintTarget."); - - // Check that argument list is writable. - if (!mTagArgumentListOpen) - throw new PaintException("XML argument list not open."); - - tagBuffer.append(" " + name + "=\"" + escapeXML(value) + "\""); - } - - /** - * Adds a string type variable. - * - * @param owner - * the Listener for variable changes. - * @param name - * the Variable name. - * @param value - * the Variable initial value. - * @throws PaintException - * if the paint operation failed. - */ - public void addVariable(VariableOwner owner, String name, String value) - throws PaintException { - String code = variableMap.registerVariable(name, String.class, value, - owner); - startTag("string"); - addAttribute(UIDL_ARG_ID, code); - addAttribute(UIDL_ARG_NAME, name); - addText(value); - endTag("string"); - } - - /** - * Adds a int type variable. - * - * @param owner - * the Listener for variable changes. - * @param name - * the Variable name. - * @param value - * the Variable initial value. - * @throws PaintException - * if the paint operation failed. - */ - public void addVariable(VariableOwner owner, String name, int value) - throws PaintException { - String code = variableMap.registerVariable(name, Integer.class, - new Integer(value), owner); - startTag("integer"); - addAttribute(UIDL_ARG_ID, code); - addAttribute(UIDL_ARG_NAME, name); - addAttribute(UIDL_ARG_VALUE, String.valueOf(value)); - endTag("integer"); - } - - /** - * Adds a boolean type variable. - * - * @param owner - * the Listener for variable changes. - * @param name - * the Variable name. - * @param value - * the Variable initial value. - * @throws PaintException - * if the paint operation failed. - */ - public void addVariable(VariableOwner owner, String name, boolean value) - throws PaintException { - String code = variableMap.registerVariable(name, Boolean.class, - new Boolean(value), owner); - startTag("boolean"); - addAttribute(UIDL_ARG_ID, code); - addAttribute(UIDL_ARG_NAME, name); - addAttribute(UIDL_ARG_VALUE, String.valueOf(value)); - endTag("boolean"); - } - - /** - * Adds a string array type variable. - * - * @param owner - * the Listener for variable changes. - * @param name - * the Variable name. - * @param value - * the Variable initial value. - * @throws PaintException - * if the paint operation failed. - */ - public void addVariable(VariableOwner owner, String name, Object[] value) - throws PaintException { - String code = variableMap.registerVariable(name, String[].class, value, - owner); - startTag("array"); - addAttribute(UIDL_ARG_ID, code); - addAttribute(UIDL_ARG_NAME, name); - for (int i = 0; i < value.length; i++) - addSection("ai", (String) value[i]); - endTag("array"); - } - - /** - * Adds a upload stream type variable. - * - * @param owner - * the Listener for variable changes. - * @param name - * the Variable name. - * - * @throws PaintException - * if the paint operation failed. - */ - public void addUploadStreamVariable(VariableOwner owner, String name) - throws PaintException { - String code = variableMap.registerVariable(name, UploadStream.class, - null, owner); - startTag("uploadstream"); - addAttribute(UIDL_ARG_ID, code); - addAttribute(UIDL_ARG_NAME, name); - endTag("uploadstream"); - } - - /** - * Prints the single text section. - *

      - * Prints full text section. The section data is escaped from XML tags and - * surrounded by XML start and end-tags. - *

      - * - * @param sectionTagName - * the name of the tag. - * @param sectionData - * the section data. - * @throws PaintException - * if the paint operation failed. - */ - public void addSection(String sectionTagName, String sectionData) - throws PaintException { - startTag(sectionTagName); - addText(sectionData); - endTag(sectionTagName); - } - - /** - * Adds XML directly to UIDL. - * - * @param xml - * the XML to be added. - * @throws PaintException - * if the paint operation failed. - */ - public void addUIDL(String xml) throws PaintException { - - // Ensure that the target is open - if (this.closed) - throw new PaintException( - "Attempted to write to a closed PaintTarget."); - - // Make sure that the open start tag is closed before - // anything is written. - ensureClosedTag(); - - // Escape and write what was given - if (xml != null) - append(xml); - - } - - /** - * Adds XML section with namespace. - * - * @see com.itmill.toolkit.terminal.PaintTarget#addXMLSection(String, - * String, String) - */ - public void addXMLSection(String sectionTagName, String sectionData, - String namespace) throws PaintException { - - // Ensures that the target is open - if (this.closed) - throw new PaintException( - "Attempted to write to a closed PaintTarget."); - - startTag(sectionTagName); - if (namespace != null) - addAttribute("xmlns", namespace); - - // Closes that starting tag - ensureClosedTag(); - - if (sectionData != null) - append(sectionData); - endTag(sectionTagName); - } - - /** - * Gets the UIDL already printed to stream. Paint target must be closed - * before the getUIDL can be called. - * - * @return the UIDL. - */ - public String getUIDL() { - if (this.closed) { - return uidlBuffer.toString(); - } - throw new IllegalStateException( - "Tried to read UIDL from open PaintTarget"); - } - - /** - * Closes the paint target. Paint target must be closed before the - * getUIDL can be called. Subsequent attempts to write to - * paint target. If the target was already closed, call to this function is - * ignored. will generate an exception. - * - * @throws PaintException - * if the paint operation failed. - */ - public void close() throws PaintException { - if (!this.closed) { - this.endTag("uidl"); - this.closed = true; - } - } - - /** - * Prints element start tag of a paintable section. Starts a paintable - * section using the given tag. The PaintTarget may implement a caching - * scheme, that checks the paintable has actually changed or can a cached - * version be used instead. This method should call the startTag method. - *

      - * If the Paintable is found in cache and this function returns true it may - * omit the content and close the tag, in which case cached content should - * be used. - *

      - * Note: Web adapter does not currently implement caching and this - * function always returns false. - * - * @param paintable - * the paintable to start. - * @param tag - * the name of the start tag. - * @return false - * @throws PaintException - * if the paint operation failed. - * @see com.itmill.toolkit.terminal.PaintTarget#startTag(Paintable, String), - * #startTag(String) - * @since 3.1 - */ - public boolean startTag(Paintable paintable, String tag) - throws PaintException { - startTag(tag); - return false; - } - - /** - * Adds CDATA node to target UIDL-tree. - * - * @param text - * the Character data to add. - * @throws PaintException - * if the paint operation failed. - * @since 3.1 - */ - public void addCharacterData(String text) throws PaintException { - addUIDL(""); - } - - public void addAttribute(String string, String[] keys) { - // TODO Auto-generated method stub - - } - - public void addAttribute(String string, Object[] keys) { - // TODO Auto-generated method stub - - } - - public void addVariable(VariableOwner owner, String name, String[] value) throws PaintException { - // TODO Auto-generated method stub - - } - -} diff --git a/src/com/itmill/toolkit/terminal/web/XSLReader.java b/src/com/itmill/toolkit/terminal/web/XSLReader.java deleted file mode 100644 index 483bd73572..0000000000 --- a/src/com/itmill/toolkit/terminal/web/XSLReader.java +++ /dev/null @@ -1,590 +0,0 @@ -/* ************************************************************************* - - 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.web; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Collection; -import java.util.Iterator; - -import org.xml.sax.Attributes; -import org.xml.sax.ContentHandler; -import org.xml.sax.DTDHandler; -import org.xml.sax.EntityResolver; -import org.xml.sax.ErrorHandler; -import org.xml.sax.InputSource; -import org.xml.sax.Locator; -import org.xml.sax.SAXException; -import org.xml.sax.SAXNotRecognizedException; -import org.xml.sax.SAXNotSupportedException; -import org.xml.sax.SAXParseException; -import org.xml.sax.XMLReader; - -/** - * Class implementing XMLReader for the UIDLTransformer. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - -public class XSLReader implements XMLReader, ContentHandler { - - static protected final int XSLT_UNKNOWN = 0; - - static protected final int XSLT_XALAN = 1; - - static protected final int XSLT_SAXON6 = 2; - - static protected final int XSLT_SAXON7 = 3; - - static protected final int XSLT_RESIN = 4; - - static protected final int XSLT_WEBLOGIC = 5; - - static protected int xsltProcessor = XSLT_UNKNOWN; - static { - String transformerName = UIDLTransformer.xsltFactory.getClass() - .getName(); - - // Saxon 7.x - if ("net.sf.saxon.TransformerFactoryImpl".equals(transformerName)) - xsltProcessor = XSLT_SAXON7; - - // Saxon 6.x - else if ("com.icl.saxon.TransformerFactoryImpl".equals(transformerName)) - xsltProcessor = XSLT_SAXON6; - - // Xalan - else if ("org.apache.xalan.processor.TransformerFactoryImpl" - .equals(transformerName) - || "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl" - .equals(transformerName)) - xsltProcessor = XSLT_XALAN; - // Resin - else if ("com.caucho.xsl.Xsl".equals(transformerName)) - xsltProcessor = XSLT_RESIN; - - else if ("weblogic.xml.jaxp.RegistrySAXTransformerFactory" - .equals(transformerName)) - xsltProcessor = XSLT_WEBLOGIC; - else { - throw new RuntimeException( - "\nThis version of IT Mill Toolkit " - + " does not support the selected XSLT-processer:\n " - + transformerName - + "\n" - + "You can specify the used XSLT processor with JVM " - + "parameter like: \n" - + " -Djavax.xml.transform.TransformerFactory=net.sf.saxon.TransformerFactoryImpl\n" - + " -Dorg.xml.sax.driver=org.apache.crimson.parser.XMLReaderImpl\n"); - } - } - - private static final String[] JAVA_PREFIX = { "java://", "millstone://" }; - - private Collection streams; - - private boolean startTagHandled = false; - - private String xslNamespace = ""; - - private ContentHandler handler; - - private XMLReader reader; - - private XSLStreamLocator locator = null; - - private Locator streamLocator = null; - - private int streamStartLineNumber = 0; - - public XSLReader(XMLReader reader, Collection streams) { - this.reader = reader; - reader.setContentHandler(this); - this.streams = streams; - } - - /** - * Parses all streams given for constructor parameter. The input parameter - * is ignored. - * - * @see org.xml.sax.XMLReader#parse(InputSource) - */ - public synchronized void parse(InputSource input) throws IOException, - SAXException { - - startTagHandled = false; - handler.startDocument(); - // Parse all files - for (Iterator i = streams.iterator(); i.hasNext();) { - ThemeSource.XSLStream xslStream = (ThemeSource.XSLStream) i.next(); - this.locator = new XSLStreamLocator(xslStream.getId()); - InputStream in = (xslStream).getStream(); - - // Parse the stream - reader.parse(new InputSource(in)); - - } - handler.endElement(xslNamespace, "stylesheet", "xsl:stylesheet"); - handler.endDocument(); - } - - /** - * @see org.xml.sax.ContentHandler#endElement(String, String, String) - */ - public void endElement(String namespaceURI, String localName, String qName) - throws SAXException { - if (localName.equals("stylesheet")) { - return; // Skip - } - handler.endElement(namespaceURI, localName, qName); - } - - /** - * @see org.xml.sax.ContentHandler#processingInstruction(String, String) - */ - public void processingInstruction(String target, String data) - throws SAXException { - handler.processingInstruction(target, data); - } - - /** - * @see org.xml.sax.ContentHandler#startElement(String, String, String, - * Attributes) - */ - public void startElement(String namespaceURI, String localName, - String qName, Attributes atts) throws SAXException { - - // Only the first stylesheet is used - if (startTagHandled && localName.equals("stylesheet")) - return; // skip - - // Get the namespace that will be used for closing the theme - if (localName.equals("stylesheet")) { - startTagHandled = true; - this.xslNamespace = namespaceURI; - - // Manage calls to external functions in XSLT-processor independent - // way, but still using XSLT 1.0 - handler.startElement(namespaceURI, localName, qName, - new AttributeMapper(atts)); - } else - - // Handle the element in superclass directly - handler.startElement(namespaceURI, localName, qName, atts); - } - - /** - * @see org.xml.sax.ContentHandler#characters(char[], int, int) - */ - public void characters(char[] ch, int start, int length) - throws SAXException { - handler.characters(ch, start, length); - } - - /** - * @see org.xml.sax.ContentHandler#startDocument() - */ - public void startDocument() throws SAXException { - // Ignore document starts - } - - /** - * @see org.xml.sax.ContentHandler#endDocument() - */ - public void endDocument() throws SAXException { - // Ignore document ends, but add previous line numbers - if (this.streamLocator != null) { - this.streamStartLineNumber += this.streamLocator.getLineNumber(); - } - } - - /** - * @see org.xml.sax.ContentHandler#endPrefixMapping(String) - */ - public void endPrefixMapping(String prefix) throws SAXException { - handler.endPrefixMapping(prefix); - } - - /** - * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int) - */ - public void ignorableWhitespace(char[] ch, int start, int length) - throws SAXException { - handler.ignorableWhitespace(ch, start, length); - } - - /** - * @see org.xml.sax.ContentHandler#setDocumentLocator(Locator) - */ - public void setDocumentLocator(Locator locator) { - this.streamLocator = locator; - // create new locator combined streams/files - if (!startTagHandled) { - handler.setDocumentLocator(this.locator); - } - } - - /** - * @see org.xml.sax.ContentHandler#skippedEntity(String) - */ - public void skippedEntity(String name) throws SAXException { - handler.skippedEntity(name); - } - - /** - * @see org.xml.sax.ContentHandler#startPrefixMapping(String, String) - */ - public void startPrefixMapping(String prefix, String uri) - throws SAXException { - handler.startPrefixMapping(prefix, uri); - } - - /** - * Overrides the default content handler. - * - * @see org.xml.sax.XMLReader#getContentHandler() - */ - public ContentHandler getContentHandler() { - return this.handler; - } - - /** - * Overrides the default content handler. - * - * @see org.xml.sax.XMLReader#setContentHandler(ContentHandler) - */ - public void setContentHandler(ContentHandler handler) { - this.handler = handler; - } - - /** - * @see org.xml.sax.XMLReader#getDTDHandler() - */ - public DTDHandler getDTDHandler() { - return reader.getDTDHandler(); - } - - /** - * @see org.xml.sax.XMLReader#getEntityResolver() - */ - public EntityResolver getEntityResolver() { - return reader.getEntityResolver(); - } - - /** - * @see org.xml.sax.XMLReader#getErrorHandler() - */ - public ErrorHandler getErrorHandler() { - return reader.getErrorHandler(); - } - - /** - * @see org.xml.sax.XMLReader#getFeature(String) - */ - public boolean getFeature(String name) throws SAXNotRecognizedException, - SAXNotSupportedException { - return reader.getFeature(name); - } - - /** - * @see org.xml.sax.XMLReader#getProperty(String) - */ - public Object getProperty(String name) throws SAXNotRecognizedException, - SAXNotSupportedException { - return reader.getProperty(name); - } - - /** - * Overrides the parse. - * - * @see org.xml.sax.XMLReader#parse(String) - */ - public void parse(String systemId) throws IOException, SAXException { - this.parse((InputSource) null); - } - - /** - * @see org.xml.sax.XMLReader#setDTDHandler(DTDHandler) - */ - public void setDTDHandler(DTDHandler handler) { - reader.setDTDHandler(handler); - } - - /** - * @see org.xml.sax.XMLReader#setEntityResolver(EntityResolver) - */ - public void setEntityResolver(EntityResolver resolver) { - reader.setEntityResolver(resolver); - } - - /** - * @see org.xml.sax.XMLReader#setErrorHandler(ErrorHandler) - */ - public void setErrorHandler(ErrorHandler handler) { - reader.setErrorHandler(new SAXStreamErrorHandler(handler)); - } - - /** - * @see org.xml.sax.XMLReader#setFeature(String, boolean) - */ - public void setFeature(String name, boolean value) - throws SAXNotRecognizedException, SAXNotSupportedException { - reader.setFeature(name, value); - } - - /** - * @see org.xml.sax.XMLReader#setProperty(String, Object) - */ - public void setProperty(String name, Object value) - throws SAXNotRecognizedException, SAXNotSupportedException { - reader.setProperty(name, value); - } - - public class AttributeMapper implements Attributes { - - private Attributes original; - - /** - * - * @param originalAttributes - */ - public AttributeMapper(Attributes originalAttributes) { - original = originalAttributes; - } - - /** - * @see org.xml.sax.Attributes#getIndex(String, String) - */ - public int getIndex(String uri, String localName) { - return original.getIndex(uri, localName); - } - - /** - * @see org.xml.sax.Attributes#getIndex(String) - */ - public int getIndex(String qName) { - return original.getIndex(qName); - } - - /** - * @see org.xml.sax.Attributes#getLength() - */ - public int getLength() { - return original.getLength(); - } - - /** - * @see org.xml.sax.Attributes#getLocalName(int) - */ - public String getLocalName(int index) { - return original.getLocalName(index); - } - - /** - * @see org.xml.sax.Attributes#getQName(int) - */ - public String getQName(int index) { - return original.getQName(index); - } - - /** - * @see org.xml.sax.Attributes#getType(int) - */ - public String getType(int index) { - return original.getType(index); - } - - /** - * @see org.xml.sax.Attributes#getType(String, String) - */ - public String getType(String uri, String localName) { - return original.getType(uri, localName); - } - - /** - * @see org.xml.sax.Attributes#getType(String) - */ - public String getType(String qName) { - return original.getType(qName); - } - - /** - * @see org.xml.sax.Attributes#getURI(int) - */ - public String getURI(int index) { - String uri = original.getURI(index); - - for (int i = 0; i < JAVA_PREFIX.length; i++) - if (uri != null && uri.startsWith(JAVA_PREFIX[i])) { - - System.out.print("DEBUG " + uri + " --> "); - switch (xsltProcessor) { - case XSLT_SAXON6: - uri = "saxon://" - + uri.substring(JAVA_PREFIX[i].length()); - break; - case XSLT_SAXON7: - uri = "saxon://" - + uri.substring(JAVA_PREFIX[i].length()); - break; - case XSLT_XALAN: - uri = "xalan://" - + uri.substring(JAVA_PREFIX[i].length()); - break; - default: - uri = "xalan://" - + uri.substring(JAVA_PREFIX[i].length()); - break; - } - System.out.println(uri); - } - - return uri; - } - - /** - * @see org.xml.sax.Attributes#getValue(int) - */ - public String getValue(int index) { - return original.getValue(index); - } - - /** - * @see org.xml.sax.Attributes#getValue(String, String) - */ - public String getValue(String uri, String localName) { - return original.getValue(uri, localName); - } - - /** - * @see org.xml.sax.Attributes#getValue(String) - */ - public String getValue(String qName) { - return original.getValue(qName); - } - } - - public class XSLStreamLocator implements Locator { - - private String id; - - /** - * - * @param id - */ - public XSLStreamLocator(String id) { - this.id = id; - } - - /** - * - * @see org.xml.sax.Locator#getPublicId() - */ - public String getPublicId() { - return streamLocator.getPublicId(); - } - - /** - * - * @see org.xml.sax.Locator#getSystemId() - */ - public String getSystemId() { - return streamLocator.getSystemId() + "" + id; - } - - /** - * - * @see org.xml.sax.Locator#getLineNumber() - */ - public int getLineNumber() { - return streamLocator.getLineNumber(); - } - - public int getCombinedLineNumber() { - return streamLocator.getLineNumber() + streamStartLineNumber; - } - - /** - * @see org.xml.sax.Locator#getColumnNumber() - */ - public int getColumnNumber() { - return streamLocator.getColumnNumber(); - } - - /** - * Gets the id. - * - * @return the id . - */ - public String getId() { - return id; - } - } - - public class SAXStreamErrorHandler implements ErrorHandler { - - private ErrorHandler handler; - - /** - * - * @param origHandler - */ - SAXStreamErrorHandler(ErrorHandler origHandler) { - this.handler = origHandler; - } - - /** - * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException) - */ - public void warning(SAXParseException exception) throws SAXException { - handler.warning(new SAXParseException("" + exception.getMessage() - + " in " + locator.getId(), locator, exception)); - } - - /** - * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException) - */ - public void error(SAXParseException exception) throws SAXException { - handler.error(new SAXParseException("" + exception.getMessage() - + " in " + locator.getId(), locator, exception)); - } - - /** - * @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException) - */ - public void fatalError(SAXParseException exception) throws SAXException { - handler.fatalError(new SAXParseException("" - + exception.getMessage() + " in " + locator.getId(), - locator, exception)); - } - } -} diff --git a/src/com/itmill/toolkit/terminal/web/package.html b/src/com/itmill/toolkit/terminal/web/package.html deleted file mode 100644 index 774ad5f5af..0000000000 --- a/src/com/itmill/toolkit/terminal/web/package.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - -

      This package implement web terminal for both AJAX-capable and more limited web browsers.

      - -

      Package Specification

      - - - - - - -