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;
private Set removedWindows = new HashSet();
- private AjaxPaintTarget paintTarget;
+ private PaintTarget paintTarget;
public AjaxApplicationManager(Application application) {
this.application = application;
application.removeListener((Application.WindowDetachListener) this);
}
+
+ public void handleUidlRequest(HttpServletRequest request,
+ HttpServletResponse response, ThemeSource themeSource) throws IOException {
+ handleUidlRequest(request,
+ response, themeSource, false);
+
+ }
+
+
/**
*
* @param request
* if the writing failed due to input/output error.
*/
public void handleUidlRequest(HttpServletRequest request,
- HttpServletResponse response, ThemeSource themeSource)
- throws IOException {
+ 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)
if (window == null)
return;
- // Sets the response type
- response.setContentType("application/xml; charset=UTF-8");
-
- paintTarget = new AjaxPaintTarget(getVariableMap(), this,
- out);
+ if(isJson) {
+ // Sets the response type
+ response.setContentType("application/json; charset=UTF-8");
+ paintTarget = new AjaxJsonPaintTarget(getVariableMap(),
+ this, out);
+ } else {
+ response.setContentType("application/xml; charset=UTF-8");
+ paintTarget = new AjaxXmlPaintTarget(getVariableMap(),
+ this, out);
+ }
// Render the removed windows
Set removed = new HashSet(getRemovedWindows());
paintTarget.addAttribute("pid", pid);
// Track paints to identify empty paints
- paintTarget.setTrackPaints(true);
+ ((AjaxPaintTarget) paintTarget).setTrackPaints(true);
p.paint(paintTarget);
// If no paints add attribute empty
- if (paintTarget.getNumberOfPaints() <= 0) {
+ if (((AjaxPaintTarget) paintTarget).getNumberOfPaints() <= 0) {
paintTarget.addAttribute("visible", false);
}
paintTarget.endTag("change");
}
}
- // add meta instruction for client to set focus if it is set
- Paintable f = (Paintable) application.consumeFocus();
- // .. or initializion (first uidl-request)
- boolean init = application.ajaxInit();
- if (init || f != null) {
- paintTarget.startTag("meta");
- if (init)
- paintTarget.addAttribute("appInit", true);
- if (f != null) {
- paintTarget.startTag("focus");
- paintTarget.addAttribute("pid", getPaintableId(f));
- paintTarget.endTag("focus");
- }
- paintTarget.endTag("meta");
- }
-
- // 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
- for (Iterator i = paintTarget.preCachedResources.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) {
- paintTarget.startTag("precache");
- paintTarget.addAttribute("resource", 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() + ")");
- }
- paintTarget.addCharacterData(layout.toString());
- paintTarget.endTag("precache");
- }
- }
-
- paintTarget.close();
+ // add meta instruction for client to set focus if it is set
+ Paintable f = (Paintable) application.consumeFocus();
+ // .. or initializion (first uidl-request)
+ boolean init = application.ajaxInit();
+ if(init || f != null) {
+ paintTarget.startTag("meta");
+ if(init)
+ paintTarget.addAttribute("appInit", true);
+ if(f != null) {
+ paintTarget.startTag("focus");
+ paintTarget.addAttribute("pid", getPaintableId(f));
+ paintTarget.endTag("focus");
+ }
+ paintTarget.endTag("meta");
+ }
+
+ // 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
+ 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) {
+ paintTarget.startTag("precache");
+ paintTarget.addAttribute("resource", 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() + ")");
+ }
+ paintTarget.addCharacterData(layout.toString());
+ paintTarget.endTag("precache");
+ }
+ }
+
+ ((AjaxPaintTarget) paintTarget).close();
out.flush();
} else {
public synchronized String getPaintableId(Paintable paintable) {
String id = (String) paintableIdMap.get(paintable);
- if (id == null)
- // get PID using growing sequence number
+ if (id == null) {
id = "PID" + Integer.toString(idSequence++);
- paintableIdMap.put(paintable, id);
+ paintableIdMap.put(paintable, id);
+ }
return id;
}
--- /dev/null
+/* *************************************************************************
+
+ 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 output
+ * A character-output stream.
+ * @throws PaintException
+ * if the paint operation failed.
+ */
+ public AjaxJsonPaintTarget(AjaxVariableMap variableMap,
+ AjaxApplicationManager manager, OutputStream output)
+ throws PaintException {
+
+ 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();
+ openJsonTags = new Stack();
+ mTagArgumentListOpen = false;
+
+ // Adds document declaration
+
+ // Adds UIDL start tag and its attributes
+ tag = new JsonTag();
+ openJsonTags.push(tag);
+ append("{");
+
+ }
+
+ /**
+ * Method append.This method is thread safe.
+ *
+ * @param string
+ * the text to insert.
+ */
+ private void append(String string) {
+ uidlBuffer.print(string);
+ }
+
+
+ public void startTag(String tagName) throws PaintException {
+ startTag(tagName, false);
+ }
+
+ /**
+ * Prints the element start tag.
+ *
+ * <pre>
+ * Todo:
+ * Checking of input values
+ *
+ * </pre>
+ *
+ * @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.");
+
+ tagName = tag.postfixChildtag(tagName, true);
+
+ // Checks tagName and attributes here
+ mOpenTags.push(tagName);
+ openJsonTags.push(tag);
+
+
+ if(isChildNode && !tag.childrenArrayOpen) {
+ append(tag.startField());
+ tag.openChildrenArray();
+ }
+ if(!isChildNode && tag.childrenArrayOpen)
+ tag.closeChildrenArray();
+
+ append(tag.startField());
+ tag = new JsonTag();
+ if(isChildNode) {
+ append("{");
+ tag.setChildNode(true);
+ } else {
+ // Prints the tag with attributes
+ append("" + tagName +": {");
+ }
+
+ mTagArgumentListOpen = true;
+
+ if ("customlayout".equals(tagName))
+ customLayoutArgumentsOpen = true;
+ }
+
+ /**
+ * 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.");
+
+ JsonTag parent = (JsonTag) openJsonTags.pop();
+ if(parent != null)
+ tagName = parent.postfixChildtag(tagName, false);
+
+ String lastTag = "";
+
+ lastTag = (String) mOpenTags.pop();
+ if (!tagName.equalsIgnoreCase(lastTag))
+ throw new PaintException("Invalid UIDL: wrong ending tag: '"
+ + tagName + "' expected: '" + lastTag + "'.");
+
+ if(tag.childrenArrayOpen)
+ tag.closeChildrenArray();
+ append(tag.getData());
+ append(tag.attributesAsJsonObject());
+ append(tag.variablesAsJsonObject());
+ // Writes the end (closing) tag
+ append("}");
+ tag = parent;
+ 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;
+ }
+
+ 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 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");
+
+
+ tag.addAttribute( name + ": \"" + escapeJSON(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 {
+ 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 from XML tags and
+ * surrounded by XML start and end-tags.
+ *
+ * @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 {
+ 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.
+
+ // Escape and write what was given
+ if (xml != null)
+ tag.addData(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;
+
+ if (sectionData != null)
+ tag.addData(sectionData);
+ endTag(sectionTagName);
+ }
+
+ /**
+ * Gets the UIDL already printed to stream. Paint target must be closed
+ * before the <code>getUIDL</code> 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
+ * <code>getUIDL</code> 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 {
+ append("}");
+ if (!this.closed) {
+ flush();
+
+ // Close all
+ this.uidlBuffer.close();
+ 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);
+ addAttribute("type", tagName);
+ 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 = true;
+
+ Vector variables = new Vector();
+
+ Vector attr = new Vector();
+
+ private HashMap childTagCounters = new HashMap();
+
+ StringBuffer data = new StringBuffer();
+
+ public boolean childrenArrayOpen = false;
+
+ private boolean childNode = false;
+
+ public JsonTag() {
+
+ }
+
+ public void openChildrenArray() {
+ if(!childrenArrayOpen) {
+ append("children : [");
+ childrenArrayOpen = true;
+ firstField = true;
+ }
+ }
+
+ /**
+ * This is used to prevent possible collapses on tag names
+ * @param tagName
+ * @param start
+ * @return
+ */
+ public String postfixChildtag(String tagName, boolean start) {
+ TagCounter i = (TagCounter) childTagCounters.get(tagName);
+ if(i == null) {
+ i = new TagCounter();
+ childTagCounters.put(tagName, i);
+ }
+ tagName = i.postfix(tagName);
+ if(!start)
+ i.increment();
+ return tagName;
+ }
+
+ 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 ",";
+ }
+ }
+
+ public void addData(String s) {
+ data.append(s);
+ }
+
+ public String getData() {
+ if(data.length() == 0)
+ return "";
+ return startField() + "data:\"" + escapeJSON(data.toString()) +"\"";
+ }
+
+ public void addAttribute(String jsonNode) {
+ attr.add(jsonNode);
+ }
+
+ public String attributesAsJsonObject() {
+ if(attr.size() == 0)
+ return "";
+ StringBuffer buf = new StringBuffer();
+ buf.append(startField());
+ buf.append("attr:{");
+ for (Iterator iter = attr.iterator(); iter.hasNext();) {
+ String element = (String) iter.next();
+ buf.append(element);
+ if(iter.hasNext())
+ buf.append(",");
+ }
+ buf.append("}");
+ return buf.toString();
+ }
+
+ public void addVariable(Variable v) {
+ variables.add(v);
+ }
+
+ public String variablesAsJsonObject() {
+ if(variables.size() == 0)
+ return "";
+ StringBuffer buf = new StringBuffer();
+ buf.append(startField());
+ buf.append("variables:{");
+ for (Iterator iter = variables.iterator(); 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 +":{name:\""+name+"\",id:\"" +
+ code+"\",type:\"boolean\", value : "
+ + (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 +":{name:\""+name+"\",id:\"" +
+ code+"\",type:\"string\", value : \""
+ + value + "\"}";
+ }
+
+ }
+
+ class IntVariable extends Variable {
+ int value;
+
+ public IntVariable(VariableOwner owner, String name, int v) {
+ value = v;
+ this.name = name;
+ code = variableMap.registerVariable(name, Boolean.class,
+ new Integer(value), owner);
+ }
+
+ public String getJsonPresentation() {
+ return name +":{name:\""+name+"\",id:\"" +
+ code+"\",type:\"int\", value : "
+ + 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 +":{name:\""+name+"\",id:\"" +
+ code+"\",type:\"array\", value : [";
+ for (int i = 0; i < value.length;) {
+ pres += value[i];
+ i++;
+ if(i < value.length)
+ pres += ",";
+ }
+ pres += "]}";
+
+ return pres;
+ }
+ }
+
+ public Set getPreCachedResources() {
+ return new HashSet();
+ }
+
+ public void setPreCachedResources(Set preCachedResources) {
+ // TODO Auto-generated method stub
+
+ }
+
+
+}
+++ /dev/null
-/* *************************************************************************
-
- 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 AjaxPaintTarget implements PaintTarget {
-
- /* Document type declarations */
- private final static String UIDL_XML_DECL = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
-
- 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;
-
- 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 AjaxPaintTarget(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);
- }
-
- /**
- * Prints the element start tag.
- *
- * <pre>
- * Todo:
- * Checking of input values
- *
- * </pre>
- *
- * @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();
-
- // 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;
- }
-
- /**
- * 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.");
-
- 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("</" + lastTag + ">");
- 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.
- *
- * <pre>
- *
- * TODO: XML checking should be made
- *
- * </pre>
- *
- * @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);
- }
-
- /**
- * 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 {
- 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) {
- 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 {
- 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");
-
- // 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))
- preCachedResources.add("layout/" + value + ".html");
- }
-
- /**
- * 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, 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");
- }
-
- /**
- * 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 to be printed.
- * @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.
- *
- * @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);
- append(">");
- mTagArgumentListOpen = false;
-
- if (sectionData != null)
- append(sectionData);
- endTag(sectionTagName);
- }
-
- /**
- * Gets the UIDL already printed to stream. Paint target must be closed
- * before the <code>getUIDL</code> 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
- * <code>getUIDL</code> 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("changes");
- flush();
-
- // Close all
- this.uidlBuffer.close();
- 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 tag)
- throws PaintException {
- startTag(tag);
- 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 {
- ensureClosedTag();
- if (text != null)
- append("<![CDATA[" + 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;
- }
-
-}
--- /dev/null
+/* *************************************************************************
+
+ 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 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+
+ 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("</" + lastTag + ">");
+ 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.
+ *
+ * <pre>
+ *
+ * TODO: XML checking should be made
+ *
+ * </pre>
+ *
+ * @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("<![CDATA[" + text + "]]>");
+ }
+
+ /* (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;
+ }
+
+}
String resourceId = request.getPathInfo();
if (resourceId != null && resourceId.startsWith(AJAX_UIDL_URI)) {
getApplicationManager(application).handleUidlRequest(
- request, response, themeSource);
+ request, response, themeSource, true);
return;
}