Save This Page
Home » zk-src-3.5.1 » org » zkoss » zk » ui » impl » [javadoc | source]
    1   /* PageImpl.java
    2   
    3   {{IS_NOTE
    4   	Purpose:
    5   		
    6   	Description:
    7   		
    8   	History:
    9   		Fri Jun  3 18:17:32     2005, Created by tomyeh
   10   }}IS_NOTE
   11   
   12   Copyright (C) 2005 Potix Corporation. All Rights Reserved.
   13   
   14   {{IS_RIGHT
   15   	This program is distributed under GPL Version 2.0 in the hope that
   16   	it will be useful, but WITHOUT ANY WARRANTY.
   17   }}IS_RIGHT
   18   */
   19   package org.zkoss.zk.ui.impl;
   20   
   21   import java.util.Iterator;
   22   import java.util.ListIterator;
   23   import java.util.List;
   24   import java.util.LinkedList;
   25   import java.util.ArrayList;
   26   import java.util.Map;
   27   import java.util.HashMap;
   28   import java.util.LinkedHashMap;
   29   import java.util.AbstractMap;
   30   import java.util.Set;
   31   import java.util.HashSet;
   32   import java.util.Collection;
   33   import java.util.Collections;
   34   import java.io.Writer;
   35   import java.io.IOException;
   36   
   37   import org.zkoss.lang.D;
   38   import org.zkoss.lang.Classes;
   39   import org.zkoss.lang.Objects;
   40   import org.zkoss.lang.Strings;
   41   import org.zkoss.lang.Exceptions;
   42   import org.zkoss.lang.Expectable;
   43   import org.zkoss.util.CollectionsX;
   44   import org.zkoss.util.logging.Log;
   45   import org.zkoss.io.Serializables;
   46   import org.zkoss.xel.ExpressionFactory;
   47   import org.zkoss.xel.VariableResolver;
   48   import org.zkoss.xel.Function;
   49   import org.zkoss.xel.FunctionMapper;
   50   import org.zkoss.xel.util.DualFunctionMapper;
   51   
   52   import org.zkoss.zk.mesg.MZk;
   53   import org.zkoss.zk.ui.WebApp;
   54   import org.zkoss.zk.ui.Desktop;
   55   import org.zkoss.zk.ui.Richlet;
   56   import org.zkoss.zk.ui.Page;
   57   import org.zkoss.zk.ui.Session;
   58   import org.zkoss.zk.ui.IdSpace;
   59   import org.zkoss.zk.ui.Component;
   60   import org.zkoss.zk.ui.Executions;
   61   import org.zkoss.zk.ui.Execution;
   62   import org.zkoss.zk.ui.UiException;
   63   import org.zkoss.zk.ui.ComponentNotFoundException;
   64   import org.zkoss.zk.ui.event.EventListener;
   65   import org.zkoss.zk.ui.event.Events;
   66   import org.zkoss.zk.ui.metainfo.PageDefinition;
   67   import org.zkoss.zk.ui.metainfo.LanguageDefinition;
   68   import org.zkoss.zk.ui.metainfo.ComponentDefinition;
   69   import org.zkoss.zk.ui.metainfo.ComponentDefinitionMap;
   70   import org.zkoss.zk.ui.metainfo.DefinitionNotFoundException;
   71   import org.zkoss.zk.ui.metainfo.ZScript;
   72   import org.zkoss.zk.ui.util.Condition;
   73   import org.zkoss.zk.ui.util.PageSerializationListener;
   74   import org.zkoss.zk.ui.sys.ExecutionCtrl;
   75   import org.zkoss.zk.ui.sys.WebAppCtrl;
   76   import org.zkoss.zk.ui.sys.DesktopCtrl;
   77   import org.zkoss.zk.ui.sys.PageCtrl;
   78   import org.zkoss.zk.ui.sys.PageConfig;
   79   import org.zkoss.zk.ui.sys.ComponentCtrl;
   80   import org.zkoss.zk.ui.sys.ComponentsCtrl;
   81   import org.zkoss.zk.ui.sys.Names;
   82   import org.zkoss.zk.ui.sys.UiEngine;
   83   import org.zkoss.zk.ui.sys.IdGenerator;
   84   import org.zkoss.zk.xel.ExValue;
   85   import org.zkoss.zk.au.out.AuSetTitle;
   86   import org.zkoss.zk.scripting.Interpreter;
   87   import org.zkoss.zk.scripting.Interpreters;
   88   import org.zkoss.zk.scripting.HierachicalAware;
   89   import org.zkoss.zk.scripting.SerializableAware;
   90   import org.zkoss.zk.scripting.Namespace;
   91   import org.zkoss.zk.scripting.InterpreterNotFoundException;
   92   import org.zkoss.zk.scripting.util.AbstractNamespace;
   93   
   94   /**
   95    * An implmentation of {@link Page} and {@link PageCtrl}.
   96    * Refer to them for more details.
   97    *
   98    * <p>Note: though {@link PageImpl} is serializable, it is designed
   99    * to work with Web container to enable the serialization of sessions.
  100    * It is not suggested to serialize and desrialize it directly since
  101    * many fields might be lost.
  102    *
  103    * <p>On the other hand, it is OK to serialize and deserialize
  104    * {@link Component}.
  105    *
  106    * <p>Implementation Notes:<br>
  107    * It is not thread-safe because it is protected by the spec:
  108    * at most one thread can access a page and all its components at the same time.
  109    *
  110    * @author tomyeh
  111    */
  112   public class PageImpl implements Page, PageCtrl, java.io.Serializable {
  113   	private static final Log log = Log.lookup(PageImpl.class);
  114   	private static final Log _zklog = Log.lookup("org.zkoss.zk.log");
  115       private static final long serialVersionUID = 20070413L;
  116   
  117   	/** URI for redrawing as a desktop or part of another desktop. */
  118   	private final ExValue _cplURI, _dkURI, _pgURI;
  119   	/** The component that includes this page, or null if not included. */
  120   	private transient Component _owner;
  121   	/** Used to retore _owner. */
  122   	private transient String _ownerUuid;
  123   	private transient Desktop _desktop;
  124   	private String _id, _uuid;
  125   	private String _title = "", _style = "";
  126   	private final String _path;
  127   	private String _zslang;
  128   	/** A list of deferred zscript [Component parent, {@link ZScript}]. */
  129   	private List _zsDeferred;
  130   	/** A list of root components. */
  131   	private final List _roots = new LinkedList();
  132   	private transient List _roRoots;
  133   	/** A map of fellows. */
  134   	private transient Map _fellows;
  135   	/** A map of attributes. */
  136   	private transient Map _attrs;
  137   	/** A map of event listener: Map(evtnm, List(EventListener)). */
  138   	private transient Map _listeners;
  139   	/** The default parent. */
  140   	private transient Component _defparent;
  141   	/** The reason to store it is PageDefinition is not serializable. */
  142   	private FunctionMapper _mapper;
  143   	/** The reason to store it is PageDefinition is not serializable. */
  144   	private ComponentDefinitionMap _compdefs;
  145   	/** The reason to store it is PageDefinition is not serializable. */
  146   	private transient LanguageDefinition _langdef;
  147   	/** The header tags. */
  148   	private String _headers = "";
  149   	/** The root attributes. */
  150   	private String _rootAttrs = "";
  151   	private String _contentType, _docType, _firstLine;
  152   	private Boolean _cacheable;
  153   	/** The expression factory (ExpressionFactory).*/
  154   	private Class _expfcls;
  155   	/** A map of interpreters Map(String zslang, Interpreter ip). */
  156   	private transient Map _ips;
  157   	private transient NS _ns;
  158   	/** A list of {@link VariableResolver}. */
  159   	private transient List _resolvers;
  160   	private boolean _complete;
  161   
  162   	/** Constructs a page by giving the page definition.
  163   	 *
  164   	 * <p>Note: when a page is constructed, it doesn't belong to a desktop
  165   	 * yet. Caller has to invoke {@link #init} to complete
  166   	 * the creation of a page.
  167   	 * Why two phase? Contructor could be called before execution
  168   	 * is activated, but {@link #init} must be called in an execution.
  169   	 *
  170   	 * <p>Also note that {@link #getId} and {@link #getTitle}
  171   	 * are not ready until {@link #init} is called.
  172   	 *
  173   	 * @param pgdef the page definition (never null).
  174   	 */
  175   	public PageImpl(PageDefinition pgdef) {
  176   		this(pgdef.getLanguageDefinition(), pgdef.getComponentDefinitionMap(),
  177   			pgdef.getRequestPath(), pgdef.getZScriptLanguage());
  178   	}
  179   	/** Constructs a page without page definition and richlet.
  180   	 *
  181   	 * @param langdef the language definition (never null)
  182   	 * @param compdefs the component definition map.
  183   	 * If null, an empty map is assumed.
  184   	 * @param path the request path. If null, empty is assumed.
  185   	 * @param zslang the zscript language. If null, "Java" is assumed.
  186   	 */
  187   	public PageImpl(LanguageDefinition langdef,
  188   	ComponentDefinitionMap compdefs, String path, String zslang) {
  189   		init();
  190   
  191   		_langdef = langdef;
  192   		_cplURI = new ExValue(_langdef.getCompleteURI(), String.class);
  193   		_dkURI = new ExValue(_langdef.getDesktopURI(), String.class);
  194   		_pgURI = new ExValue(_langdef.getPageURI(), String.class);
  195   		_compdefs = compdefs != null ? compdefs:
  196   			new ComponentDefinitionMap(
  197   				_langdef.getComponentDefinitionMap().isCaseInsensitive());
  198   		_path = path != null ? path: "";
  199   		_zslang = zslang != null ? zslang: "Java";
  200   	}
  201   
  202   	/** Constructs a page by specifying a richlet.
  203   	 *
  204   	 * <p>Note: when a page is constructed, it doesn't belong to a desktop
  205   	 * yet. Caller has to invoke {@link #init} to complete
  206   	 * the creation of a page.
  207   	 *
  208   	 * <p>Also note that {@link #getId} and {@link #getTitle}
  209   	 * are not ready until {@link #init} is called.
  210   	 *
  211   	 * @param richlet the richlet to serve this page.
  212   	 * @param path the request path, or null if not available
  213   	 */
  214   	public PageImpl(Richlet richlet, String path) {
  215   		init();
  216   
  217   		_langdef = richlet.getLanguageDefinition();
  218   		_cplURI = new ExValue(_langdef.getCompleteURI(), String.class);
  219   		_dkURI = new ExValue(_langdef.getDesktopURI(), String.class);
  220   		_pgURI = new ExValue(_langdef.getPageURI(), String.class);
  221   		_compdefs = new ComponentDefinitionMap(
  222   			_langdef.getComponentDefinitionMap().isCaseInsensitive());
  223   		_path = path != null ? path: "";
  224   		_zslang = "Java";
  225   	}
  226   	/** Initialized the page when contructed or deserialized.
  227   	 */
  228   	protected void init() {
  229   		_ips = new LinkedHashMap(3);
  230   		_ns = new NS();
  231   		_roRoots = Collections.unmodifiableList(_roots);
  232   		_attrs = new HashMap();
  233   		_fellows = new HashMap();
  234   	}
  235   
  236   	/** Returns the UI engine.
  237   	 */
  238   	private final UiEngine getUiEngine() {
  239   		return ((WebAppCtrl)_desktop.getWebApp()).getUiEngine();
  240   	}
  241   
  242   	//-- Page --//
  243   	private final Execution getExecution() {
  244   		return _desktop != null ? _desktop.getExecution():
  245   			Executions.getCurrent();
  246   	}
  247   	public final FunctionMapper getFunctionMapper() {
  248   		return _mapper;
  249   	}
  250   	public void addFunctionMapper(FunctionMapper mapper) {
  251   		_mapper = DualFunctionMapper.combine(mapper, _mapper);
  252   	}
  253   
  254   	public String getRequestPath() {
  255   		return _path;
  256   	}
  257   	public final String getId() {
  258   		return _id;
  259   	}
  260   	public final String getUuid() {
  261   		return _uuid;
  262   	}
  263   	public void setId(String id) {
  264   		if (_desktop != null)
  265   			throw new UiException("Unable to change the identifier after the page is initialized");
  266   		if (id != null && id.length() > 0) _id = id;
  267   		//No need to update client since it is allowed only before init(...)
  268   	}
  269   	public String getTitle() {
  270   		return _title;
  271   	}
  272   	public void setTitle(String title) {
  273   		if (title == null) title = "";
  274   		if (!_title.equals(title)) {
  275   			_title = title;
  276   			if (_desktop != null) {
  277   				final Execution exec = getExecution();
  278   				if (_title.length() > 0) {
  279   					_title = (String)exec.evaluate(this, _title, String.class);
  280   					if (_title == null) _title = "";
  281   				}
  282   
  283   				if (exec.isAsyncUpdate(this))
  284   					getUiEngine().addResponse("setTitle", new AuSetTitle(_title));
  285   			}
  286   		}
  287   	}
  288   	public String getStyle() {
  289   		return _style;
  290   	}
  291   	public void setStyle(String style) {
  292   		if (style == null) style = "";
  293   		if (!_style.equals(style)) {
  294   			_style = style;
  295   			if (_desktop != null) {
  296   				final Execution exec = getExecution();
  297   				if (_style.length() > 0) {
  298   					_style = (String)exec.evaluate(this, _style, String.class);
  299   					if (_style == null) _style = "";
  300   				}
  301   				//FUTURE: might support the change of style dynamically
  302   			}
  303   		}
  304   	}
  305   
  306   	public Collection getRoots() {
  307   		return _roRoots;
  308   	}
  309   
  310   	public Map getAttributes(int scope) {
  311   		switch (scope) {
  312   		case DESKTOP_SCOPE:
  313   			return _desktop != null ?
  314   				_desktop.getAttributes(): Collections.EMPTY_MAP;
  315   		case SESSION_SCOPE:
  316   			return _desktop != null ?
  317   				_desktop.getSession().getAttributes(): Collections.EMPTY_MAP;
  318   		case APPLICATION_SCOPE:
  319   			return _desktop != null ?
  320   				_desktop.getWebApp().getAttributes(): Collections.EMPTY_MAP;
  321   		case PAGE_SCOPE:
  322   			return _attrs;
  323   		case REQUEST_SCOPE:
  324   			final Execution exec = getExecution();
  325   			if (exec != null) return exec.getAttributes();
  326   			//fall thru
  327   		default:
  328   			return Collections.EMPTY_MAP;
  329   		}
  330   	}
  331   	public Object getAttribute(String name, int scope) {
  332   		return getAttributes(scope).get(name);
  333   	}
  334   	public Object setAttribute(String name, Object value, int scope) {
  335   		if (value != null) {
  336   			final Map attrs = getAttributes(scope);
  337   			if (attrs == Collections.EMPTY_MAP)
  338   				throw new IllegalStateException("This component doesn't belong to any ID space: "+this);
  339   			return attrs.put(name, value);
  340   		} else {
  341   			return removeAttribute(name, scope);
  342   		}
  343   	}
  344   	public Object removeAttribute(String name, int scope) {
  345   			final Map attrs = getAttributes(scope);
  346   			if (attrs == Collections.EMPTY_MAP)
  347   				throw new IllegalStateException("This component doesn't belong to any ID space: "+this);
  348   		return attrs.remove(name);
  349   	}
  350   
  351   	public Map getAttributes() {
  352   		return _attrs;
  353   	}
  354   	public Object getAttribute(String name) {
  355   		return _attrs.get(name);
  356   	}
  357   	public Object setAttribute(String name, Object value) {
  358   		return value != null ? _attrs.put(name, value): removeAttribute(name);
  359   	}
  360   	public Object removeAttribute(String name) {
  361   		return _attrs.remove(name);
  362   	}
  363   
  364   	public void invalidate() {
  365   		getUiEngine().addInvalidate(this);
  366   	}
  367   	public void removeComponents() {
  368   		for (Iterator it = new ArrayList(getRoots()).iterator();
  369   		it.hasNext();)
  370   			((Component)it.next()).detach();
  371   	}
  372   
  373   	public Class resolveClass(String clsnm) throws ClassNotFoundException {
  374   		try {
  375   			return Classes.forNameByThread(clsnm);
  376   		} catch (ClassNotFoundException ex) {
  377   			for (Iterator it = getLoadedInterpreters().iterator();
  378   			it.hasNext();) {
  379   				final Class c = ((Interpreter)it.next()).getClass(clsnm);
  380   				if (c != null)
  381   					return c;
  382   			}
  383   			throw ex;
  384   		}
  385   	}
  386   	public void setVariable(String name, Object val) {
  387   		_ns.setVariable(name, val, true);
  388   	}
  389   	public boolean containsVariable(String name) {
  390   		return _ns.containsVariable(name, true);
  391   	}
  392   	public Object getVariable(String name) {
  393   		return _ns.getVariable(name, true);
  394   	}
  395   	public void unsetVariable(String name) {
  396   		_ns.unsetVariable(name, true);
  397   	}
  398   	public Class getZScriptClass(String clsnm) {
  399   		for (Iterator it = getLoadedInterpreters().iterator();
  400   		it.hasNext();) {
  401   			Class cls = ((Interpreter)it.next()).getClass(clsnm);
  402   			if (cls != null)
  403   				return cls;
  404   		}
  405   
  406   		try {
  407   			return Classes.forNameByThread(clsnm);
  408   		} catch (ClassNotFoundException ex) {
  409   			return null;
  410   		}
  411   	}
  412   	public Function getZScriptFunction(String name, Class[] argTypes) {
  413   		for (Iterator it = getLoadedInterpreters().iterator();
  414   		it.hasNext();) {
  415   			Function mtd =
  416   				((Interpreter)it.next()).getFunction(name, argTypes);
  417   			if (mtd != null)
  418   				return mtd;
  419   		}
  420   		return null;
  421   	}
  422   	public Function getZScriptFunction(Namespace ns, String name, Class[] argTypes) {
  423   		for (Iterator it = getLoadedInterpreters().iterator();
  424   		it.hasNext();) {
  425   			final Object ip = it.next();
  426   			Function mtd =
  427   				ip instanceof HierachicalAware ?
  428   				((HierachicalAware)ip).getFunction(ns, name, argTypes):
  429   				((Interpreter)ip).getFunction(name, argTypes);
  430   			if (mtd != null)
  431   				return mtd;
  432   		}
  433   		return null;
  434   	}
  435   	public Function getZScriptFunction(
  436   	Component comp, String name, Class[] argTypes) {
  437   		return getZScriptFunction(comp != null ? comp.getNamespace(): null,
  438   			name, argTypes);
  439   	}
  440   	/** @deprecated As of release 3.0.0, replaced by {@link #getZScriptFunction(String,Class[])}.
  441   	 */
  442   	public org.zkoss.zk.scripting.Method
  443   	getZScriptMethod(String name, Class[] argTypes) {
  444   		final Function fun = getZScriptFunction(name, argTypes);
  445   		return fun != null ? new FuncMethod(fun): null;
  446   	}
  447   	/** @deprecated As of release 3.0.0, replaced by {@link #getZScriptFunction(String,Class[])}.
  448   	 */
  449   	public org.zkoss.zk.scripting.Method
  450   	getZScriptMethod(Namespace ns, String name, Class[] argTypes) {
  451   		final Function fun = getZScriptFunction(ns, name, argTypes);
  452   		return fun != null ? new FuncMethod(fun): null;
  453   	}
  454   	public Object getZScriptVariable(String name) {
  455   		for (Iterator it = getLoadedInterpreters().iterator();
  456   		it.hasNext();) {
  457   			final Object val = ((Interpreter)it.next()).getVariable(name);
  458   			if (val != null)
  459   				return val;
  460   		}
  461   		return null;
  462   	}
  463   	public Object getZScriptVariable(Namespace ns, String name) {
  464   		for (Iterator it = getLoadedInterpreters().iterator();
  465   		it.hasNext();) {
  466   			final Object ip = it.next();
  467   			final Object val = ip instanceof HierachicalAware ?
  468   				((HierachicalAware)ip).getVariable(ns, name):
  469   				((Interpreter)ip).getVariable(name);
  470   			if (val != null)
  471   				return val;
  472   		}
  473   		return null;
  474   	}
  475   	public Object getZScriptVariable(Component comp, String name) {
  476   		return getZScriptVariable(comp != null ? comp.getNamespace(): null,
  477   			name);
  478   	}
  479   
  480   	public Object getXelVariable(String name) {
  481   		final VariableResolver resolv =
  482   			getExecution().getVariableResolver();
  483   		return resolv != null ? resolv.resolveVariable(name): null;
  484   	}
  485   	/** @deprecated As of release of 3.0.0, replaced with {@link #getXelVariable}.
  486   	 */
  487   	public Object getELVariable(String name) {
  488   		return getXelVariable(name);
  489   	}
  490   
  491   	/** Resolves the variable defined in variable resolvers.
  492   	 */
  493   	private Object resolveVariable(String name) {
  494   		if (_resolvers != null) {
  495   			for (Iterator it = _resolvers.iterator(); it.hasNext();) {
  496   				Object o = ((VariableResolver)it.next()).resolveVariable(name);
  497   				if (o != null)
  498   					return o;
  499   			}
  500   		}
  501   		return null;
  502   	}
  503   	public boolean addVariableResolver(VariableResolver resolver) {
  504   		if (resolver == null)
  505   			throw new IllegalArgumentException("null");
  506   
  507   		if (_resolvers == null)
  508   			_resolvers = new LinkedList();
  509   		else if (_resolvers.contains(resolver))
  510   			return false;
  511   
  512   		_resolvers.add(0, resolver); //FILO order
  513   		return true;
  514   	}
  515   	public boolean removeVariableResolver(VariableResolver resolver) {
  516   		return _resolvers != null && _resolvers.remove(resolver);
  517   	}
  518   
  519   	public boolean addEventListener(String evtnm, EventListener listener) {
  520   		if (evtnm == null || listener == null)
  521   			throw new IllegalArgumentException("null");
  522   		if (!Events.isValid(evtnm))
  523   			throw new IllegalArgumentException("Invalid event name: "+evtnm);
  524   
  525   		if (_listeners == null)
  526   			_listeners = new HashMap(3);
  527   
  528   		List l = (List)_listeners.get(evtnm);
  529   		if (l != null) {
  530   			for (Iterator it = l.iterator(); it.hasNext();) {
  531   				final EventListener li = (EventListener)it.next();
  532   				if (listener.equals(li))
  533   					return false;
  534   			}
  535   		} else {
  536   			_listeners.put(evtnm, l = new LinkedList());
  537   		}
  538   		l.add(listener);
  539   		return true;
  540   	}
  541   	public boolean removeEventListener(String evtnm, EventListener listener) {
  542   		if (evtnm == null || listener == null)
  543   			throw new NullPointerException();
  544   
  545   		if (_listeners != null) {
  546   			final List l = (List)_listeners.get(evtnm);
  547   			if (l != null) {
  548   				for (Iterator it = l.iterator(); it.hasNext();) {
  549   					final EventListener li = (EventListener)it.next();
  550   					if (listener.equals(li)) {
  551   						if (l.size() == 1)
  552   							_listeners.remove(evtnm);
  553   						else
  554   							it.remove();
  555   						return true;
  556   					}
  557   				}
  558   			}
  559   		}
  560   		return false;
  561   	}
  562   
  563   	public Component getFellow(String compId) {
  564   		final Component comp = (Component)_fellows.get(compId);
  565   		if (comp == null)
  566   			if (compId != null && ComponentsCtrl.isAutoId(compId))
  567   				throw new ComponentNotFoundException(MZk.AUTO_ID_NOT_LOCATABLE, compId);
  568   			else
  569   				throw new ComponentNotFoundException("Fellow component not found: "+compId);
  570   		return comp;
  571   	}
  572   	public Component getFellowIfAny(String compId) {
  573   		return (Component)_fellows.get(compId);
  574   	}
  575   	public Collection getFellows() {
  576   		return Collections.unmodifiableCollection(_fellows.values());
  577   	}
  578   
  579   	public boolean isComplete() {
  580   		return _complete;
  581   	}
  582   	public void setComplete(boolean complete) {
  583   		_complete = complete;
  584   	}
  585   
  586   	//-- PageCtrl --//
  587   	public void init(PageConfig config) {
  588   		if (_desktop != null)
  589   			throw new IllegalStateException("Don't init twice");
  590   
  591   		final Execution exec = Executions.getCurrent();
  592   		_desktop = exec.getDesktop();
  593   		if (_desktop == null)
  594   			throw new IllegalArgumentException("null desktop");
  595   
  596   		initVariables();
  597   
  598   		if (((ExecutionCtrl)exec).isRecovering()) {
  599   			final String uuid = config.getUuid(), id = config.getId();
  600   			if (uuid == null || id == null)
  601   				throw new IllegalArgumentException("both id and uuid are required in recovering");
  602   			_uuid = uuid;
  603   			_id = id;
  604   		} else {
  605   			final IdGenerator idgen =
  606   				((WebAppCtrl)_desktop.getWebApp()).getIdGenerator();
  607   			if (idgen != null)
  608   				_uuid = idgen.nextPageUuid(this);
  609   			if (_uuid == null)
  610   				_uuid = ((DesktopCtrl)_desktop).getNextUuid();
  611   
  612   			if (_id == null) {
  613   				final String id = config.getId();
  614   				if (id != null && id.length() != 0) _id = id;
  615   			}
  616   			if (_id != null)
  617   				_id = (String)exec.evaluate(this, _id, String.class);
  618   			if (_id != null && _id.length() != 0) {
  619   				final String INVALID = ".&\\%";
  620   				if (Strings.anyOf(_id, INVALID, 0) < _id.length())
  621   					throw new IllegalArgumentException("Invalid page ID: "+_id+". Invalid characters: "+INVALID);
  622   			} else {
  623   				_id = _uuid;
  624   			}
  625   		}
  626   
  627   		String s = config.getHeaders();
  628   		if (s != null) _headers = s;
  629   
  630   		if (_title.length() == 0) {
  631   			s = config.getTitle();
  632   			if (s != null) setTitle(s);
  633   		}
  634   		if (_style.length() == 0) {
  635   			s = config.getStyle();
  636   			if (s != null) setStyle(s);
  637   		}
  638   
  639   		((DesktopCtrl)_desktop).addPage(this);	
  640   	}
  641   	private void initVariables() {
  642   		setVariable("log", _zklog);
  643   		setVariable("page", this);
  644   		setVariable("pageScope", getAttributes());
  645   		setVariable("requestScope", REQUEST_ATTRS);
  646   		setVariable("spaceOwner", this);
  647   
  648   		if (_desktop != null) {
  649   			setVariable("desktop", _desktop);
  650   			setVariable("desktopScope", _desktop.getAttributes());
  651   			final WebApp wapp = _desktop.getWebApp();
  652   			setVariable("application", wapp);
  653   			setVariable("applicationScope", wapp.getAttributes());
  654   			final Session sess = _desktop.getSession();
  655   			setVariable("session", sess);
  656   			setVariable("sessionScope", sess.getAttributes());
  657   		}
  658   	}
  659   	public void destroy() {
  660   		for (Iterator it = getLoadedInterpreters().iterator(); it.hasNext();) {
  661   			final Interpreter ip = (Interpreter)it.next();
  662   			try {
  663   				ip.destroy();
  664   			} catch (Throwable ex) {
  665   				log.error("Failed to destroy "+ip, ex);
  666   			}
  667   		}
  668   		_ips.clear();
  669   
  670   		//theorectically, the following is not necessary, but, to be safe...
  671   		_roots.clear();
  672   		_attrs.clear();
  673   		_fellows = new HashMap(1); //not clear() since # of fellows might huge
  674   		_ips = null; //not clear since it is better to NPE than memory leak
  675   		_desktop = null;
  676   		_owner = _defparent = null;
  677   		_listeners = null;
  678   		_ns = null;
  679   		_resolvers = null;
  680   	}
  681   
  682   	private static final Map REQUEST_ATTRS = new AbstractMap() {
  683   		public Set entrySet() {
  684   			final Execution exec = Executions.getCurrent();
  685   			if (exec == null) return Collections.EMPTY_SET;
  686   			return exec.getAttributes().entrySet();
  687   		}
  688   		public Object put(Object name, Object value) {
  689   			final Execution exec = Executions.getCurrent();
  690   			if (exec == null) throw new IllegalStateException("No execution at all");
  691   			return exec.getAttributes().put(name, value);
  692   		}
  693   		public boolean containsKey(Object name) {
  694   			final Execution exec = Executions.getCurrent();
  695   			return exec != null && exec.getAttributes().containsKey(name);
  696   		}
  697   		public Object get(Object name) {
  698   			final Execution exec = Executions.getCurrent();
  699   			if (exec == null) return null;
  700   			return exec.getAttributes().get(name);
  701   		}
  702   		public Object remove(Object name) {
  703   			final Execution exec = Executions.getCurrent();
  704   			if (exec == null) return null;
  705   			return exec.getAttributes().remove(name);
  706   		}
  707   	};
  708   
  709   	public String getHeaders() {
  710   		return _headers;
  711   	}
  712   	public String getRootAttributes() {
  713   		return _rootAttrs;
  714   	}
  715   	public void setRootAttributes(String rootAttrs) {
  716   		_rootAttrs = rootAttrs != null ? rootAttrs: "";
  717   	}
  718   	public String getContentType() {
  719   		return _contentType;
  720   	}
  721   	public void setContentType(String contentType) {
  722   		_contentType = contentType;
  723   	}
  724   	public String getDocType() {
  725   		return _docType;
  726   	}
  727   	public void setDocType(String docType) {
  728   		_docType = docType;
  729   	}
  730   	public String getFirstLine() {
  731   		return _firstLine;
  732   	}
  733   	public void setFirstLine(String firstLine) {
  734   		_firstLine = firstLine;
  735   	}
  736   	public Boolean getCacheable() {
  737   		return _cacheable;
  738   	}
  739   	public void setCacheable(Boolean cacheable) {
  740   		_cacheable = cacheable;
  741   	}
  742   
  743   	public final Desktop getDesktop() {
  744   		return _desktop;
  745   	}
  746   	public void addRoot(Component comp) {
  747   		assert D.OFF || comp.getParent() == null;
  748   		for (Iterator it = _roots.iterator(); it.hasNext();)
  749   			if (comp == it.next())
  750   				return;
  751   		_roots.add(comp);
  752   	}
  753   	public void removeRoot(Component comp) {
  754   		for (Iterator it = _roots.iterator(); it.hasNext();)
  755   			if (comp == it.next()) {
  756   				it.remove();
  757   				return;
  758   			}
  759   	}
  760   	public void moveRoot(Component comp, Component refRoot) {
  761   		if (comp.getPage() != this || comp.getParent() != null)
  762   			return; //nothing to do
  763   
  764   		boolean added = false, found = false;
  765   		for (ListIterator it = _roots.listIterator(); it.hasNext();) {
  766   			final Object o = it.next();
  767   			if (o == comp) {
  768   				if (!added) {
  769   					if (!it.hasNext()) return; //last
  770   					if (it.next() == refRoot) return; //same position
  771   					it.previous(); it.previous(); it.next(); //restore cursor
  772   				}
  773   				it.remove();
  774   				found = true;
  775   				if (added || refRoot == null) break; //done
  776   			} else if (o == refRoot) {
  777   				it.previous();
  778   				it.add(comp);
  779   				it.next();
  780   				added = true;
  781   				if (found) break; //done
  782   			}
  783   		}
  784   
  785   		if (!added) _roots.add(comp);
  786   	}
  787   
  788   	public void addFellow(Component comp) {
  789   		final String compId = comp.getId();
  790   		assert D.OFF || !ComponentsCtrl.isAutoId(compId);
  791   
  792   		final Object old = _fellows.put(compId, comp);
  793   		if (old != comp) { //possible due to recursive call
  794   			if (old != null) {
  795   				_fellows.put(((Component)old).getId(), old); //recover
  796   				throw new InternalError("Called shall prevent replicated ID for roots");
  797   			}
  798   		}
  799   	}
  800   	public void removeFellow(Component comp) {
  801   		_fellows.remove(comp.getId());
  802   	}
  803   	public boolean hasFellow(String compId) {
  804   		return _fellows.containsKey(compId);
  805   	}
  806   
  807   	public void redraw(Collection responses, Writer out) throws IOException {
  808   		if (log.debugable()) log.debug("Redrawing page: "+this+", roots="+_roots);
  809   
  810   		final Execution exec = getExecution();
  811   		final ExecutionCtrl execCtrl = (ExecutionCtrl)exec;
  812   		final boolean asyncUpdate = execCtrl.getVisualizer().isEverAsyncUpdate();
  813   		final boolean bIncluded = asyncUpdate || exec.isIncluded()
  814   			|| exec.getAttribute(ATTR_REDRAW_BY_INCLUDE) != null;
  815   		final String uri = (String)
  816   			(_complete ? _cplURI: bIncluded ? _pgURI: _dkURI)
  817   				.getValue(_langdef.getEvaluator(), this);
  818   				//desktop and page URI is defined in language
  819   
  820   		final Map attrs = new HashMap(6);
  821   		attrs.put("page", this);
  822   		attrs.put("asyncUpdate", Boolean.valueOf(asyncUpdate));
  823   		attrs.put("action", "/"); //A non-empty string (backward compatible)
  824   		attrs.put("responses",
  825   			responses != null ? responses: Collections.EMPTY_LIST);
  826   		if (bIncluded) {
  827   			attrs.put("included", "true");
  828   				//maintain original state since desktop will include page...
  829   			exec.include(out, uri, attrs, Execution.PASS_THRU_ATTR);
  830   		} else {
  831   //FUTURE: Consider if config.isKeepDesktopAcrossVisits() implies cacheable
  832   //Why yes: the client doesn't need to ask the server for updated content
  833   //Why no: browsers seems fail to handle DHTML correctly (when BACK to
  834   //a DHTML page), so it is better to let the server handle cache, if any
  835   			final boolean cacheable =
  836   				_cacheable != null ?  _cacheable.booleanValue():
  837   					_desktop.getDevice().isCacheable();
  838   			if (!cacheable) {
  839   				//Bug 1520444
  840   				execCtrl.setHeader("Pragma", "no-cache");
  841   				execCtrl.addHeader("Cache-Control", "no-cache");
  842   				execCtrl.addHeader("Cache-Control", "no-store");
  843   				//execCtrl.addHeader("Cache-Control", "private");
  844   				//execCtrl.addHeader("Cache-Control", "max-age=0");
  845   				//execCtrl.addHeader("Cache-Control", "s-maxage=0");
  846   				//execCtrl.addHeader("Cache-Control", "must-revalidate");
  847   				//execCtrl.addHeader("Cache-Control", "proxy-revalidate");
  848   				//execCtrl.addHeader("Cache-Control", "post-check=0");
  849   				//execCtrl.addHeader("Cache-Control", "pre-check=0");
  850   				execCtrl.setHeader("Expires", "-1");
  851   
  852   				exec.setAttribute(Attributes.NO_CACHE, Boolean.TRUE);
  853   				//so ZkFns.outLangJavaScripts generates zk.keepDesktop correctly
  854   			}
  855   			exec.forward(out, uri, attrs, Execution.PASS_THRU_ATTR);
  856   				//Don't use include. Otherwise, headers will be gone.
  857   		}
  858   	}
  859   
  860   	public final Namespace getNamespace() {
  861   		return _ns;
  862   	}
  863   	public void interpret(String zslang, String script, Namespace ns) {
  864   		getInterpreter(zslang).interpret(script, ns);
  865   	}
  866   	public Interpreter getInterpreter(String zslang) {
  867   		zslang = (zslang != null ? zslang: _zslang).toLowerCase();
  868   		Interpreter ip = (Interpreter)_ips.get(zslang);
  869   		if (ip == null) {
  870   			ip = Interpreters.newInterpreter(zslang, this);
  871   			_ips.put(zslang, ip);
  872   				//set first to avoid dead loop if script calls interpret again
  873   
  874   			final String script = _langdef.getInitScript(zslang);
  875   			if (script != null) {
  876   				_ns.setVariable("log", _zklog, true);
  877   				_ns.setVariable("page", this, true);
  878   				ip.interpret(script, _ns);
  879   			}
  880   
  881   			//evaluate deferred zscripts, if any
  882   			try {
  883   				evalDeferredZScripts(ip, zslang);
  884   			} catch (IOException ex) {
  885   				throw new UiException(ex);
  886   			}
  887   		}
  888   		return ip;
  889   	}
  890   
  891   	public Collection getLoadedInterpreters() {
  892   		return _ips != null ? _ips.values(): Collections.EMPTY_LIST; //just in case
  893   	}
  894   	public String getZScriptLanguage() {
  895   		return _zslang;
  896   	}
  897   	public void setZScriptLanguage(String zslang)
  898   	throws InterpreterNotFoundException {
  899   		if (!Objects.equals(zslang, _zslang)) {
  900   			if (!Interpreters.exists(zslang))
  901   				throw new InterpreterNotFoundException(zslang, MZk.NOT_FOUND, zslang);
  902   			_zslang = zslang;
  903   		}
  904   	}
  905   	public void addDeferredZScript(Component parent, ZScript zscript) {
  906   		if (zscript != null) {
  907   			if (_zsDeferred == null)
  908   				_zsDeferred = new LinkedList();
  909   			_zsDeferred.add(new Object[] {parent, zscript});
  910   		}
  911   	}
  912   	/** Evaluates the deferred zscript.
  913   	 * It is called when the interpreter is loaded
  914   	 */
  915   	private void evalDeferredZScripts(Interpreter ip, String zslang)
  916   	throws IOException {
  917   		if (_zsDeferred != null) {
  918   			for (Iterator it = _zsDeferred.iterator(); it.hasNext();) {
  919   				final Object[] zsInfo = (Object[])it.next();
  920   				final ZScript zscript = (ZScript)zsInfo[1];
  921   				String targetlang = zscript.getLanguage();
  922   				if (targetlang == null)
  923   					targetlang = _zslang; //use default
  924   
  925   				if (targetlang.equalsIgnoreCase(zslang)) { //case insensitive
  926   					it.remove(); //done
  927   
  928   					final Component parent = (Component)zsInfo[0];
  929   					if ((parent == null || parent.getPage() == this)
  930   					&& isEffective(zscript, parent)) {
  931   						ip.interpret(zscript.getContent(this, parent),
  932   							parent != null ? parent.getNamespace(): _ns);
  933   					}
  934   				}
  935   			}
  936   
  937   			if (_zsDeferred.isEmpty())
  938   				_zsDeferred = null;
  939   		}
  940   	}
  941   	private boolean isEffective(Condition cond, Component comp) {
  942   		return comp != null ? cond.isEffective(comp): cond.isEffective(this);
  943   	}
  944   
  945   	public boolean isListenerAvailable(String evtnm) {
  946   		if (_listeners != null) {
  947   			final List l = (List)_listeners.get(evtnm);
  948   			return l != null && !l.isEmpty();
  949   		}
  950   		return false;
  951   	}
  952   	public Iterator getListenerIterator(String evtnm) {
  953   		if (_listeners != null) {
  954   			final List l = (List)_listeners.get(evtnm);
  955   			if (l != null)
  956   				return new ListenerIterator(l);
  957   		}
  958   		return CollectionsX.EMPTY_ITERATOR;
  959   	}
  960   
  961   	public final Component getOwner() {
  962   		return _owner;
  963   	}
  964   	public final void setOwner(Component comp) {
  965   		if (_owner != null)
  966   			throw new IllegalStateException("owner can be set only once");
  967   		_owner = comp;
  968   	}
  969   
  970    	public Component getDefaultParent() {
  971    		return _defparent;
  972    	}
  973    	public void setDefaultParent(Component comp) {
  974    		_defparent = comp;
  975    	}
  976   
  977   	public void sessionWillPassivate(Desktop desktop) {
  978   		for (Iterator it = _roots.iterator(); it.hasNext();)
  979   			((ComponentCtrl)it.next()).sessionWillPassivate(this);
  980   	}
  981   	public void sessionDidActivate(Desktop desktop) {
  982   		_desktop = desktop;
  983   
  984   		if (_ownerUuid != null) {
  985   			_owner = _desktop.getComponentByUuid(_ownerUuid);
  986   			_ownerUuid = null;
  987   		}
  988   
  989   		for (Iterator it = _roots.iterator(); it.hasNext();)
  990   			((ComponentCtrl)it.next()).sessionDidActivate(this);
  991   
  992   		initVariables(); //since some variables depend on desktop
  993   	}
  994   
  995   	public LanguageDefinition getLanguageDefinition() {
  996   		return _langdef;
  997   	}
  998   	public ComponentDefinitionMap getComponentDefinitionMap() {
  999   		return _compdefs;
 1000   	}
 1001   	public ComponentDefinition getComponentDefinition(String name, boolean recur) {
 1002   		final ComponentDefinition compdef = _compdefs.get(name);
 1003   		if (!recur || compdef != null)
 1004   			return compdef;
 1005   
 1006   		try {
 1007   			return _langdef.getComponentDefinition(name);
 1008   		} catch (DefinitionNotFoundException ex) {
 1009   		}
 1010   		return null;
 1011   	}
 1012   	public ComponentDefinition getComponentDefinition(Class cls, boolean recur) {
 1013   		final ComponentDefinition compdef = _compdefs.get(cls);
 1014   		if (!recur || compdef != null)
 1015   			return compdef;
 1016   
 1017   		try {
 1018   			return _langdef.getComponentDefinition(cls);
 1019   		} catch (DefinitionNotFoundException ex) {
 1020   		}
 1021   		return null;
 1022   	}
 1023   	public Class getExpressionFactoryClass() {
 1024   		return _expfcls;
 1025   	}
 1026   	public void setExpressionFactoryClass(Class expfcls) {
 1027   		if (expfcls != null && !ExpressionFactory.class.isAssignableFrom(expfcls))
 1028   			throw new IllegalArgumentException(expfcls+" must implement "+ExpressionFactory.class);
 1029   		_expfcls = expfcls;
 1030   	}
 1031   
 1032   	//-- Serializable --//
 1033   	//NOTE: they must be declared as private
 1034   	private synchronized void writeObject(java.io.ObjectOutputStream s)
 1035   	throws java.io.IOException {
 1036   		s.defaultWriteObject();
 1037   
 1038   		s.writeObject(_langdef != null ? _langdef.getName(): null);
 1039   		s.writeObject(_owner != null ? _owner.getUuid(): null);
 1040   		s.writeObject(_defparent != null ? _defparent.getUuid(): null);
 1041   
 1042   		willSerialize(_attrs.values());
 1043   		Serializables.smartWrite(s, _attrs);
 1044   
 1045   		if (_listeners != null)
 1046   			for (Iterator it = _listeners.entrySet().iterator(); it.hasNext();) {
 1047   				final Map.Entry me = (Map.Entry)it.next();
 1048   				s.writeObject(me.getKey());
 1049   
 1050   				final Collection ls = (Collection)me.getValue();
 1051   				willSerialize(ls);
 1052   				Serializables.smartWrite(s, ls);
 1053   			}
 1054   		s.writeObject(null);
 1055   
 1056   		willSerialize(_resolvers);
 1057   		Serializables.smartWrite(s, _resolvers);
 1058   
 1059   		//handle namespace
 1060   		for (Iterator it = _ns._vars.entrySet().iterator(); it.hasNext();) {
 1061   			final Map.Entry me = (Map.Entry)it.next();
 1062   			final String nm = (String)me.getKey();
 1063   			final Object val = me.getValue();
 1064   			willSerialize(val); //always called even not serializable
 1065   
 1066   			if (isVariableSerializable(nm, val)
 1067   			&& (val instanceof java.io.Serializable || val instanceof java.io.Externalizable)) {
 1068   				s.writeObject(nm);
 1069   				s.writeObject(val);
 1070   			}
 1071   		}
 1072   		s.writeObject(null); //denote end-of-namespace
 1073   
 1074   		//Handles interpreters
 1075   		for (Iterator it = _ips.entrySet().iterator(); it.hasNext();) {
 1076   			final Map.Entry me = (Map.Entry)it.next();
 1077   			final Object ip = me.getValue();
 1078   			if (ip instanceof SerializableAware) {
 1079   				s.writeObject((String)me.getKey()); //zslang
 1080   
 1081   				((SerializableAware)ip).write(s,
 1082   					new SerializableAware.Filter() {
 1083   						public boolean accept(String name, Object value) {
 1084   							return isVariableSerializable(name, value);
 1085   						}
 1086   					});
 1087   			}
 1088   		}
 1089   		s.writeObject(null); //denote end-of-interpreters
 1090   	}
 1091   	private void willSerialize(Collection c) {
 1092   		if (c != null)
 1093   			for (Iterator it = c.iterator(); it.hasNext();)
 1094   				willSerialize(it.next());
 1095   	}
 1096   	private void willSerialize(Object o) {
 1097   		if (o instanceof PageSerializationListener)
 1098   			((PageSerializationListener)o).willSerialize(this);
 1099   	}
 1100   	private synchronized void readObject(java.io.ObjectInputStream s)
 1101   	throws java.io.IOException, ClassNotFoundException {
 1102   		s.defaultReadObject();
 1103   
 1104   		init();
 1105   
 1106   		final String langnm = (String)s.readObject();
 1107   		if (langnm != null)
 1108   			_langdef = LanguageDefinition.lookup(langnm);
 1109   
 1110   		_ownerUuid = (String)s.readObject();
 1111   			//_owner is restored later when sessionDidActivate is called
 1112   
 1113   		final String pid = (String)s.readObject();
 1114   		if (pid != null)
 1115   			_defparent = fixDefaultParent(_roots, pid);
 1116   
 1117   		Serializables.smartRead(s, _attrs);
 1118   		didDeserialize(_attrs.values());
 1119   
 1120   		for (;;) {
 1121   			final String evtnm = (String)s.readObject();
 1122   			if (evtnm == null) break; //no more
 1123   
 1124   			if (_listeners == null) _listeners = new HashMap();
 1125   			final Collection ls = Serializables.smartRead(s, (Collection)null);
 1126   			_listeners.put(evtnm, ls);
 1127   			didDeserialize(ls);
 1128   		}
 1129   
 1130   		_resolvers = (List)Serializables.smartRead(s, _resolvers); //might be null
 1131   		didDeserialize(_resolvers);
 1132   
 1133   		//handle namespace
 1134   		initVariables();
 1135   		for (;;) {
 1136   			final String nm = (String)s.readObject();
 1137   			if (nm == null) break; //no more
 1138   
 1139   			Object val = s.readObject();
 1140   			_ns.setVariable(nm, val, true);
 1141   			didDeserialize(val);
 1142   		}
 1143   
 1144   		fixFellows(_roots);
 1145   
 1146   		//Handles interpreters
 1147   		for (;;) {
 1148   			final String zslang = (String)s.readObject();
 1149   			if (zslang == null) break; //no more
 1150   
 1151   			((SerializableAware)getInterpreter(zslang)).read(s);
 1152   		}
 1153   	}
 1154   	private void didDeserialize(Collection c) {
 1155   		if (c != null)
 1156   			for (Iterator it = c.iterator(); it.hasNext();)
 1157   				didDeserialize(it.next());
 1158   	}
 1159   	private void didDeserialize(Object o) {
 1160   		if (o instanceof PageSerializationListener)
 1161   			((PageSerializationListener)o).didDeserialize(this);
 1162   	}
 1163   	private static boolean isVariableSerializable(String name, Object value) {
 1164   		return !_nonSerNames.contains(name) && !(value instanceof Component);
 1165   	}
 1166   	private final static Set _nonSerNames = new HashSet();
 1167   	static {
 1168   		final String[] nms = {"log", "page", "desktop", "pageScope", "desktopScope",
 1169   			"applicationScope", "requestScope", "spaceOwner",
 1170   		"session", "sessionScope"};
 1171   		for (int j = 0; j < nms.length; ++j)
 1172   			_nonSerNames.add(nms[j]);
 1173   	}
 1174   	private final void fixFellows(Collection c) {
 1175   		for (Iterator it = c.iterator(); it.hasNext();) {
 1176   			final Component comp = (Component)it.next();
 1177   			final String compId = comp.getId();
 1178   			if (!ComponentsCtrl.isAutoId(compId))
 1179   				addFellow(comp);
 1180   			if (!(comp instanceof IdSpace))
 1181   				fixFellows(comp.getChildren()); //recursive
 1182   		}
 1183   	}
 1184   	private static final
 1185   	Component fixDefaultParent(Collection c, String uuid) {
 1186   		for (Iterator it = c.iterator(); it.hasNext();) {
 1187   			Component comp = (Component)it.next();
 1188   			if (uuid.equals(comp.getUuid()))
 1189   				return comp; //found
 1190   
 1191   			comp = fixDefaultParent(comp.getChildren(), uuid);
 1192   			if (comp != null) return comp;
 1193   		}
 1194   		return null;
 1195   	}
 1196   
 1197   	//-- Object --//
 1198   	public String toString() {
 1199   		return "[Page "+_id+']';
 1200   	}
 1201   
 1202   	private class NS extends AbstractNamespace {
 1203   		private final Map _vars = new HashMap();
 1204   
 1205   		//Namespace//
 1206   		public Component getOwner() {
 1207   			return null;
 1208   		}
 1209   		public Page getOwnerPage() {
 1210   			return PageImpl.this;
 1211   		}
 1212   		public Set getVariableNames() {
 1213   			return _vars.keySet();
 1214   		}
 1215   		public boolean containsVariable(String name, boolean local) {
 1216   			return _vars.containsKey(name) || _fellows.containsKey(name)
 1217   				|| resolveVariable(name) != null;
 1218   		}
 1219   		public Object getVariable(String name, boolean local) {
 1220   			Object val = _vars.get(name);
 1221   			if (val != null || _vars.containsKey(name))
 1222   				return val;
 1223   
 1224   			val = _fellows.get(name);
 1225   			return val != null ? val: resolveVariable(name);
 1226   		}
 1227   		public void setVariable(String name, Object value, boolean local) {
 1228   			_vars.put(name, value);
 1229   			notifyAdd(name, value);
 1230   		}
 1231   		public void unsetVariable(String name, boolean local) {
 1232   			_vars.remove(name);
 1233   			notifyRemove(name);
 1234   		}
 1235   
 1236   		public Namespace getParent() {
 1237   			return null;
 1238   		}
 1239   		public void setParent(Namespace parent) {
 1240   			throw new UnsupportedOperationException();
 1241   		}
 1242   	}
 1243   }

Save This Page
Home » zk-src-3.5.1 » org » zkoss » zk » ui » impl » [javadoc | source]