0001: /* AbstractComponent.java
0002:
0003: {{IS_NOTE
0004: Purpose:
0005:
0006: Description:
0007:
0008: History:
0009: Mon May 30 21:49:42 2005, Created by tomyeh
0010: }}IS_NOTE
0011:
0012: Copyright (C) 2005 Potix Corporation. All Rights Reserved.
0013:
0014: {{IS_RIGHT
0015: This program is distributed under GPL Version 2.0 in the hope that
0016: it will be useful, but WITHOUT ANY WARRANTY.
0017: }}IS_RIGHT
0018: */
0019: package org.zkoss.zk.ui;
0020:
0021: import java.util.Iterator;
0022: import java.util.ListIterator;
0023: import java.util.Set;
0024: import java.util.HashSet;
0025: import java.util.List;
0026: import java.util.AbstractSequentialList;
0027: import java.util.LinkedList;
0028: import java.util.Collection;
0029: import java.util.Collections;
0030: import java.util.Map;
0031: import java.util.HashMap;
0032: import java.io.Writer;
0033: import java.io.IOException;
0034:
0035: import org.zkoss.lang.D;
0036: import org.zkoss.lang.Classes;
0037: import org.zkoss.lang.Objects;
0038: import org.zkoss.util.CollectionsX;
0039: import org.zkoss.util.logging.Log;
0040: import org.zkoss.io.Serializables;
0041: import org.zkoss.xml.HTMLs;
0042:
0043: import org.zkoss.zk.mesg.MZk;
0044: import org.zkoss.zk.ui.event.EventListener;
0045: import org.zkoss.zk.ui.event.Deferrable;
0046: import org.zkoss.zk.ui.event.Event;
0047: import org.zkoss.zk.ui.event.ForwardEvent;
0048: import org.zkoss.zk.ui.event.Events;
0049: import org.zkoss.zk.ui.ext.Macro;
0050: import org.zkoss.zk.ui.ext.RawId;
0051: import org.zkoss.zk.ui.ext.NonFellow;
0052: import org.zkoss.zk.ui.ext.render.ZidRequired;
0053: import org.zkoss.zk.ui.render.ComponentRenderer;
0054: import org.zkoss.zk.ui.util.ComponentSerializationListener;
0055: import org.zkoss.zk.ui.util.ComponentCloneListener;
0056: import org.zkoss.zk.ui.util.DeferredValue;
0057: import org.zkoss.zk.ui.sys.ExecutionCtrl;
0058: import org.zkoss.zk.ui.sys.ExecutionsCtrl;
0059: import org.zkoss.zk.ui.sys.ComponentCtrl;
0060: import org.zkoss.zk.ui.sys.ComponentsCtrl;
0061: import org.zkoss.zk.ui.sys.PageCtrl;
0062: import org.zkoss.zk.ui.sys.DesktopCtrl;
0063: import org.zkoss.zk.ui.sys.SessionCtrl;
0064: import org.zkoss.zk.ui.sys.WebAppCtrl;
0065: import org.zkoss.zk.ui.sys.UiEngine;
0066: import org.zkoss.zk.ui.sys.IdGenerator;
0067: import org.zkoss.zk.ui.sys.Names;
0068: import org.zkoss.zk.ui.metainfo.AnnotationMap;
0069: import org.zkoss.zk.ui.metainfo.Annotation;
0070: import org.zkoss.zk.ui.metainfo.EventHandlerMap;
0071: import org.zkoss.zk.ui.metainfo.ComponentDefinition;
0072: import org.zkoss.zk.ui.metainfo.PageDefinition;
0073: import org.zkoss.zk.ui.metainfo.LanguageDefinition;
0074: import org.zkoss.zk.ui.metainfo.ComponentInfo;
0075: import org.zkoss.zk.ui.metainfo.ComponentDefinition;
0076: import org.zkoss.zk.ui.metainfo.ComponentDefinitionMap;
0077: import org.zkoss.zk.ui.metainfo.DefinitionNotFoundException;
0078: import org.zkoss.zk.ui.metainfo.EventHandler;
0079: import org.zkoss.zk.ui.metainfo.ZScript;
0080: import org.zkoss.zk.ui.impl.ListenerIterator;
0081: import org.zkoss.zk.fn.ZkFns;
0082: import org.zkoss.zk.au.AuResponse;
0083: import org.zkoss.zk.au.out.AuClientInfo;
0084: import org.zkoss.zk.scripting.Namespace;
0085: import org.zkoss.zk.scripting.Interpreter;
0086: import org.zkoss.zk.scripting.util.SimpleNamespace;
0087:
0088: /**
0089: * A skeletal implementation of {@link Component}. Though it is OK
0090: * to implement Component from scratch, this class simplifies some of
0091: * the chores.
0092: *
0093: * @author tomyeh
0094: */
0095: public class AbstractComponent implements Component, ComponentCtrl,
0096: java.io.Serializable {
0097: // private static final Log log = Log.lookup(AbstractComponent.class);
0098: private static final long serialVersionUID = 20070920L;
0099:
0100: private transient Page _page;
0101: private String _id;
0102: private String _uuid;
0103: private transient ComponentDefinition _def;
0104: /** The mold (default: "default"). */
0105: private String _mold = "default";
0106: /** The info of the ID space, or null if IdSpace is NOT implemented. */
0107: private transient SpaceInfo _spaceInfo;
0108: private transient Map _attrs;
0109: //don't create it dynamically because _ip bind it at constructor
0110: /** A map of event listener: Map(evtnm, EventListener)). */
0111: private transient Map _listeners;
0112: /** The extra controls. */
0113: private transient Object _xtrl;
0114:
0115: /** The list used for {@link #getChildren} only. */
0116: private transient List _apiChildren;
0117: private transient AbstractComponent _parent;
0118: /** The next sibling. */
0119: private transient AbstractComponent _next;
0120: /** The previous sibling. */
0121: private transient AbstractComponent _prev;
0122: /** The first child. */
0123: private transient AbstractComponent _first;
0124: /** The last child. */
0125: private transient AbstractComponent _last;
0126: /** # of children. */
0127: private int _nChild;
0128: /** The modification count used to avoid co-modification of _next, _prev..
0129: */
0130: private transient int _modCntChd;
0131: /** A set of components that are being removed.
0132: * It is used to prevent dead-loop between {@link #removeChild}
0133: * and {@link #setParent}.
0134: */
0135: private transient Set _rming;
0136: /** A set of components that are being added.
0137: * It is used to prevent dead-loop between {@link #insertBefore}
0138: * and {@link #setParent}.
0139: */
0140: private transient Set _adding;
0141:
0142: /** A map of annotations. Serializable since a component might have
0143: * its own annotations.
0144: */
0145: private AnnotationMap _annots;
0146: /** A map of event handler to handle events. */
0147: private EventHandlerMap _evthds;
0148: /** A map of forward conditions:
0149: * Map(String orgEvt, [listener, List([target or targetPath,targetEvent])]).
0150: */
0151: private transient Map _forwards;
0152: /** Whether _annots is shared with other components. */
0153: private transient boolean _annotsShared;
0154: /** Whether _evthds is shared with other components. */
0155: private transient boolean _evthdsShared;
0156: /** Whether this component is visible. */
0157: private boolean _visible = true;
0158:
0159: /** Constructs a component with auto-generated ID.
0160: */
0161: protected AbstractComponent() {
0162: final Execution exec = Executions.getCurrent();
0163:
0164: final Object curInfo = ComponentsCtrl.getCurrentInfo();
0165: if (curInfo != null) {
0166: ComponentsCtrl.setCurrentInfo((ComponentInfo) null); //to avoid mis-use
0167: if (curInfo instanceof ComponentInfo) {
0168: final ComponentInfo compInfo = (ComponentInfo) curInfo;
0169: _def = compInfo.getComponentDefinition();
0170: addSharedAnnotationMap(_def.getAnnotationMap());
0171: addSharedAnnotationMap(compInfo.getAnnotationMap());
0172: } else {
0173: _def = (ComponentDefinition) curInfo;
0174: addSharedAnnotationMap(_def.getAnnotationMap());
0175: }
0176: } else {
0177: _def = lookupDefinition(exec, getClass());
0178: if (_def != null)
0179: addSharedAnnotationMap(_def.getAnnotationMap());
0180: else if ((this instanceof Macro)
0181: && System
0182: .getProperty("org.zkoss.zk.MacroNoDefinitionAllowed") == null)
0183: //3.0.3: check addition prop to allow user to maintain backward compatibility
0184: throw new DefinitionNotFoundException(
0185: "Component definition not found for the macro "
0186: + this .getClass()
0187: + ". Current page definition: "
0188: + (exec != null ? ""
0189: + ((ExecutionCtrl) exec)
0190: .getCurrentPageDefinition()
0191: : "n/a")
0192: + ". Current page: "
0193: + (exec != null ? ""
0194: + ((ExecutionCtrl) exec)
0195: .getCurrentPage()
0196: : "n/a"));
0197: else
0198: _def = ComponentsCtrl.DUMMY;
0199: }
0200:
0201: init(false);
0202:
0203: _spaceInfo = this instanceof IdSpace ? new SpaceInfo(this )
0204: : null;
0205:
0206: // if (D.ON && log.debugable()) log.debug("Create comp: "+this);
0207: }
0208:
0209: private static final ComponentDefinition lookupDefinition(
0210: Execution exec, Class cls) {
0211: if (exec != null) {
0212: final ExecutionCtrl execCtrl = (ExecutionCtrl) exec;
0213: final PageDefinition pgdef = execCtrl
0214: .getCurrentPageDefinition();
0215: final Page page = execCtrl.getCurrentPage();
0216:
0217: final ComponentDefinition compdef = pgdef != null ? pgdef
0218: .getComponentDefinition(cls, true)
0219: : page != null ? page.getComponentDefinition(cls,
0220: true) : null;
0221: if (compdef != null)
0222: return compdef;
0223:
0224: return lookupDefinitionByDeviceType(exec.getDesktop()
0225: .getDeviceType(), cls);
0226: }
0227:
0228: for (Iterator it = LanguageDefinition.getDeviceTypes()
0229: .iterator(); it.hasNext();) {
0230: final ComponentDefinition compdef = lookupDefinitionByDeviceType(
0231: (String) it.next(), cls);
0232: if (compdef != null)
0233: return compdef;
0234: }
0235: return null;
0236: }
0237:
0238: private static final ComponentDefinition lookupDefinitionByDeviceType(
0239: String deviceType, Class cls) {
0240: for (Iterator it = LanguageDefinition.getByDeviceType(
0241: deviceType).iterator(); it.hasNext();) {
0242: final LanguageDefinition ld = (LanguageDefinition) it
0243: .next();
0244: try {
0245: return ld.getComponentDefinition(cls);
0246: } catch (DefinitionNotFoundException ex) { //ignore
0247: }
0248: }
0249: return null;
0250: }
0251:
0252: /** Initialize for contructor and serialization.
0253: * @param cloning whether this method is called by clone()
0254: */
0255: private void init(boolean cloning) {
0256: _apiChildren = new AbstractSequentialList() {
0257: public int size() {
0258: return _nChild;
0259: }
0260:
0261: public ListIterator listIterator(int index) {
0262: return new ChildIter(index);
0263: }
0264: };
0265:
0266: if (!cloning)
0267: _attrs = new HashMap(4);
0268: }
0269:
0270: /** Adds to the ID spaces, if any, when ID is changed.
0271: * Caller has to make sure the uniqueness.
0272: */
0273: private static void addToIdSpaces(final Component comp) {
0274: if (comp instanceof IdSpace)
0275: ((AbstractComponent) comp).bindToIdSpace(comp);
0276:
0277: final IdSpace is = getSpaceOwnerOfParent(comp);
0278: if (is instanceof Component)
0279: ((AbstractComponent) is).bindToIdSpace(comp);
0280: else if (is != null)
0281: ((PageCtrl) is).addFellow(comp);
0282: }
0283:
0284: private static final IdSpace getSpaceOwnerOfParent(Component comp) {
0285: final Component parent = comp.getParent();
0286: if (parent != null)
0287: return parent.getSpaceOwner();
0288: else
0289: return comp.getPage();
0290: }
0291:
0292: /** Removes from the ID spaces, if any, when ID is changed. */
0293: private static void removeFromIdSpaces(final Component comp) {
0294: final AbstractComponent abcomp = (AbstractComponent) comp;
0295: final String compId = abcomp._id;
0296: if (compId == null || (comp instanceof NonFellow)
0297: || ComponentsCtrl.isAutoId(compId))
0298: return; //nothing to do
0299:
0300: if (comp instanceof IdSpace)
0301: abcomp.unbindFromIdSpace(compId);
0302:
0303: final IdSpace is = getSpaceOwnerOfParent(comp);
0304: if (is instanceof Component)
0305: ((AbstractComponent) is).unbindFromIdSpace(compId);
0306: else if (is != null)
0307: ((PageCtrl) is).removeFellow(comp);
0308: }
0309:
0310: /** Checks the uniqueness in ID space when changing ID. */
0311: private static void checkIdSpaces(final AbstractComponent comp,
0312: String newId) {
0313: if (comp instanceof NonFellow)
0314: return; //no need to check
0315:
0316: if (comp instanceof IdSpace
0317: && comp._spaceInfo.fellows.containsKey(newId))
0318: throw new UiException("Not unique in the ID space of "
0319: + comp);
0320:
0321: final IdSpace is = getSpaceOwnerOfParent(comp);
0322: if (is instanceof Component) {
0323: if (((AbstractComponent) is)._spaceInfo.fellows
0324: .containsKey(newId))
0325: throw new UiException("Not unique in the ID space of "
0326: + is);
0327: } else if (is != null) {
0328: if (((PageCtrl) is).hasFellow(newId))
0329: throw new UiException("Not unique in the ID space of "
0330: + is);
0331: }
0332: }
0333:
0334: /** Adds its descendants to the ID space when parent or page is changed,
0335: * excluding comp.
0336: */
0337: private static void addToIdSpacesDown(Component comp) {
0338: final IdSpace is = getSpaceOwnerOfParent(comp);
0339: if (is instanceof Component)
0340: addToIdSpacesDown(comp, (Component) is);
0341: else if (is != null)
0342: addToIdSpacesDown(comp, (PageCtrl) is);
0343: }
0344:
0345: private static void addToIdSpacesDown(Component comp,
0346: Component owner) {
0347: if (!(comp instanceof NonFellow)
0348: && !ComponentsCtrl.isAutoId(comp.getId()))
0349: ((AbstractComponent) owner).bindToIdSpace(comp);
0350: if (!(comp instanceof IdSpace))
0351: for (Iterator it = comp.getChildren().iterator(); it
0352: .hasNext();)
0353: addToIdSpacesDown((Component) it.next(), owner); //recursive
0354: }
0355:
0356: private static void addToIdSpacesDown(Component comp, PageCtrl owner) {
0357: if (!(comp instanceof NonFellow)
0358: && !ComponentsCtrl.isAutoId(comp.getId()))
0359: owner.addFellow(comp);
0360: if (!(comp instanceof IdSpace))
0361: for (Iterator it = comp.getChildren().iterator(); it
0362: .hasNext();)
0363: addToIdSpacesDown((Component) it.next(), owner); //recursive
0364: }
0365:
0366: /** Adds its descendants to the ID space when parent or page is changed,
0367: * excluding comp.
0368: */
0369: private static void removeFromIdSpacesDown(Component comp) {
0370: final IdSpace is = getSpaceOwnerOfParent(comp);
0371: if (is instanceof Component)
0372: removeFromIdSpacesDown(comp, (Component) is);
0373: else if (is != null)
0374: removeFromIdSpacesDown(comp, (PageCtrl) is);
0375: }
0376:
0377: private static void removeFromIdSpacesDown(Component comp,
0378: Component owner) {
0379: final String compId = comp.getId();
0380: if (!(comp instanceof NonFellow)
0381: && !ComponentsCtrl.isAutoId(compId))
0382: ((AbstractComponent) owner).unbindFromIdSpace(compId);
0383: if (!(comp instanceof IdSpace))
0384: for (Iterator it = comp.getChildren().iterator(); it
0385: .hasNext();)
0386: removeFromIdSpacesDown((Component) it.next(), owner); //recursive
0387: }
0388:
0389: private static void removeFromIdSpacesDown(Component comp,
0390: PageCtrl owner) {
0391: if (!(comp instanceof NonFellow)
0392: && !ComponentsCtrl.isAutoId(comp.getId()))
0393: owner.removeFellow(comp);
0394: if (!(comp instanceof IdSpace))
0395: for (Iterator it = comp.getChildren().iterator(); it
0396: .hasNext();)
0397: removeFromIdSpacesDown((Component) it.next(), owner); //recursive
0398: }
0399:
0400: /** Checks the uniqueness in ID space when changing parent. */
0401: private static void checkIdSpacesDown(Component comp,
0402: Component newparent) {
0403: final IdSpace is = newparent.getSpaceOwner();
0404: if (is instanceof Component)
0405: checkIdSpacesDown(comp, ((AbstractComponent) is)._spaceInfo);
0406: else if (is != null)
0407: checkIdSpacesDown(comp, (PageCtrl) is);
0408: }
0409:
0410: /** Checks comp and its descendants for the specified SpaceInfo. */
0411: private static void checkIdSpacesDown(Component comp, SpaceInfo si) {
0412: final String compId = comp.getId();
0413: if (!(comp instanceof NonFellow)
0414: && !ComponentsCtrl.isAutoId(compId)
0415: && si.fellows.containsKey(compId))
0416: throw new UiException("Not unique in the new ID space: "
0417: + compId);
0418: if (!(comp instanceof IdSpace))
0419: for (Iterator it = comp.getChildren().iterator(); it
0420: .hasNext();)
0421: checkIdSpacesDown((Component) it.next(), si); //recursive
0422: }
0423:
0424: /** Checks comp and its descendants for the specified page. */
0425: private static void checkIdSpacesDown(Component comp,
0426: PageCtrl pageCtrl) {
0427: final String compId = comp.getId();
0428: if (!(comp instanceof NonFellow)
0429: && !ComponentsCtrl.isAutoId(compId)
0430: && pageCtrl.hasFellow(compId))
0431: throw new UiException("Not unique in the ID space of "
0432: + pageCtrl + ": " + compId);
0433: if (!(comp instanceof IdSpace))
0434: for (Iterator it = comp.getChildren().iterator(); it
0435: .hasNext();)
0436: checkIdSpacesDown((Component) it.next(), pageCtrl); //recursive
0437: }
0438:
0439: /** Bind comp to this ID space (owned by this component).
0440: * Called only if IdSpace is implemented.
0441: */
0442: private void bindToIdSpace(Component comp) {
0443: final String compId = comp.getId();
0444: _spaceInfo.fellows.put(compId, comp);
0445: }
0446:
0447: /** Unbind comp from this ID space (owned by this component).
0448: * Called only if IdSpace is implemented.
0449: */
0450: private void unbindFromIdSpace(String compId) {
0451: _spaceInfo.fellows.remove(compId);
0452: }
0453:
0454: //-- Extra utlities --//
0455: /** Returns the mold URI based on {@link #getMold}
0456: * and the molds defined in the component definition
0457: * ({@link ComponentDefinition}).
0458: *
0459: * <p>As of release 3.0.0, it may return a String instance representing
0460: * the URI, or a {@link ComponentRenderer} instance responsible for
0461: * redrawing.
0462: *
0463: * <p>Used only for component implementation.
0464: */
0465: protected Object getMoldURI() {
0466: return _def.getMoldURI(this , getMold());
0467: }
0468:
0469: /** Returns the UI engine based on {@link #_page}'s getDesktop().
0470: * Don't call this method when _page is null.
0471: */
0472: private final UiEngine getThisUiEngine() {
0473: return ((WebAppCtrl) _page.getDesktop().getWebApp())
0474: .getUiEngine();
0475: }
0476:
0477: //-- Component --//
0478: public final Page getPage() {
0479: return _page;
0480: }
0481:
0482: public final Desktop getDesktop() {
0483: return _page != null ? _page.getDesktop() : null;
0484: }
0485:
0486: public void setPage(Page page) {
0487: if (page != _page)
0488: setPageBefore(page, null); //append
0489: }
0490:
0491: public void setPageBefore(Page page, Component refRoot) {
0492: if (refRoot != null
0493: && (page == null || refRoot == this
0494: || refRoot.getParent() != null || refRoot
0495: .getPage() != page))
0496: refRoot = null;
0497:
0498: if (_parent != null)
0499: throw new UiException(
0500: "Only the parent of a root component can be changed: "
0501: + this );
0502:
0503: final boolean samepg = page == _page;
0504: if (!samepg) {
0505: if (page != null) {
0506: if (_page != null
0507: && _page.getDesktop() != page.getDesktop())
0508: throw new UiException(
0509: "The new page must be in the same desktop: "
0510: + page);
0511: //Not allow developers to access two desktops simutaneously
0512: checkIdSpacesDown(this , (PageCtrl) page);
0513:
0514: //No need to check UUID since checkIdSpacesDown covers it
0515: //-- a page is an ID space
0516: } else { //detach from a page
0517: checkDetach(_page);
0518: }
0519:
0520: if (_page != null)
0521: removeFromIdSpacesDown(this );
0522: }
0523:
0524: addMoved(_parent, _page, page); //Not depends on UUID
0525: if (!samepg)
0526: setPage0(page); //UUID might be changed here
0527: if (page != null && (samepg || refRoot != null))
0528: ((PageCtrl) page).moveRoot(this , refRoot);
0529:
0530: if (!samepg && _page != null)
0531: addToIdSpacesDown(this );
0532: }
0533:
0534: /** Checks whether it is OK to detach the specified page.
0535: * @param page the page to detach (never null).
0536: */
0537: private static void checkDetach(Page page) {
0538: final Execution exec = Executions.getCurrent();
0539: if (exec == null)
0540: throw new UiException(
0541: "You cannot access a desktop other than an event listener");
0542: if (page.getDesktop() != exec.getDesktop())
0543: throw new UiException(
0544: "You cannot access components belong to other desktop");
0545: }
0546:
0547: /** Called when this component is moved from the specified parent
0548: * and/or page to the new page.
0549: *
0550: * <p>Default: it notifies {@link UiEngine} to update the component
0551: * at the client (usually remove-and-add).
0552: *
0553: * <p>It is designed to let derived classes overriding this method
0554: * to disable this update. However, you rarely need to override it.
0555: * One possible but rare case: the component's
0556: * visual part at the client updates the visual representation
0557: * at the client and then notify the component at the server
0558: * to update its children accordingly. In this case, it is redudant
0559: * if we ask UI Engine to send the updates to client.
0560: *
0561: * @param oldparent the parent before moved.
0562: * The new parent can be found by calling {@link #getParent}.
0563: * @param oldpg the parent before moved.
0564: * @param newpg the new page. {@link #getPage} might return
0565: * the old page.
0566: */
0567: protected void addMoved(Component oldparent, Page oldpg, Page newpg) {
0568: final Desktop dt;
0569: if (oldpg != null)
0570: dt = oldpg.getDesktop();
0571: else if (newpg != null)
0572: dt = newpg.getDesktop();
0573: else
0574: return;
0575:
0576: ((WebAppCtrl) dt.getWebApp()).getUiEngine().addMoved(this ,
0577: oldparent, oldpg, newpg);
0578: }
0579:
0580: /** Ses the page without fixing IdSpace
0581: */
0582: private void setPage0(Page page) {
0583: if (page == _page)
0584: return; //nothing changed
0585:
0586: //assert D.OFF || _parent == null || _parent.getPage() == page;
0587: //detach
0588: final boolean bRoot = _parent == null;
0589: if (_page != null) {
0590: if (bRoot)
0591: ((PageCtrl) _page).removeRoot(this );
0592: if (page == null) {
0593: ((DesktopCtrl) _page.getDesktop())
0594: .removeComponent(this );
0595: }
0596: }
0597:
0598: final Page oldpage = _page;
0599: _page = page;
0600:
0601: if (_page != null) {
0602: if (bRoot)
0603: ((PageCtrl) _page).addRoot(this ); //Not depends on uuid
0604: final Desktop desktop = _page.getDesktop();
0605: if (oldpage == null) {
0606: if (_uuid == null
0607: || _uuid == ComponentsCtrl.ANONYMOUS_ID
0608: || desktop.getComponentByUuidIfAny(_uuid) != null)
0609: _uuid = nextUuid(desktop);
0610: if (_id == null || (this instanceof RawId))
0611: _id = _uuid;
0612: //no need to handle ID space since it is either
0613: //anonymous or uuid is not changed
0614:
0615: ((DesktopCtrl) desktop).addComponent(this ); //depends on uuid
0616: }
0617:
0618: onPageAttached(_page, oldpage);
0619: } else {
0620: onPageDetached(oldpage);
0621: }
0622:
0623: if (_spaceInfo != null && _parent == null)
0624: _spaceInfo.ns.setParent(page != null ? page.getNamespace()
0625: : null);
0626:
0627: //process all children recursively
0628: for (AbstractComponent p = _first; p != null; p = p._next)
0629: p.setPage0(page); //recursive
0630: }
0631:
0632: private String nextUuid(Desktop desktop) {
0633: final IdGenerator idgen = ((WebAppCtrl) desktop.getWebApp())
0634: .getIdGenerator();
0635:
0636: String uuid;
0637: do {
0638: uuid = idgen != null ? idgen.nextComponentUuid(desktop,
0639: this ) : null;
0640: if (uuid == null)
0641: uuid = ((DesktopCtrl) desktop).getNextUuid();
0642: } while (desktop.getComponentByUuidIfAny(uuid) != null);
0643: return uuid;
0644: }
0645:
0646: public String getId() {
0647: if (_id == null)
0648: _id = getUuid();
0649: return _id;
0650: }
0651:
0652: public void setId(String id) {
0653: if (id == null || id.length() == 0)
0654: throw new UiException("ID cannot be empty");
0655:
0656: if (!Objects.equals(_id, id)) {
0657: if (Names.isReserved(id)
0658: || (!(this instanceof NonFellow) && ComponentsCtrl
0659: .isAutoId(id)))
0660: throw new UiException("Invalid ID: " + id
0661: + ". Cause: reserved words not allowed: "
0662: + Names.getReservedNames());
0663:
0664: final boolean rawId = this instanceof RawId;
0665: if (rawId
0666: && _page != null
0667: && _page.getDesktop().getComponentByUuidIfAny(id) != null)
0668: throw new UiException(
0669: "Replicated ID is not allowed for "
0670: + getClass()
0671: + ": "
0672: + id
0673: + "\nNote: HTML/WML tags, ID must be unique");
0674:
0675: checkIdSpaces(this , id);
0676:
0677: removeFromIdSpaces(this );
0678: if (rawId) { //we have to change UUID
0679: if (_page != null) {
0680: getThisUiEngine().addUuidChanged(this , false);
0681: //called before uuid is changed
0682: ((DesktopCtrl) _page.getDesktop())
0683: .removeComponent(this );
0684: }
0685:
0686: _uuid = _id = id;
0687:
0688: if (_page != null) {
0689: ((DesktopCtrl) _page.getDesktop())
0690: .addComponent(this );
0691: addMoved(_parent, _page, _page);
0692: }
0693: } else {
0694: _id = id;
0695: }
0696: addToIdSpaces(this );
0697:
0698: final Object xc = getExtraCtrl();
0699: if ((xc instanceof ZidRequired)
0700: && ((ZidRequired) xc).isZidRequired())
0701: smartUpdate("z.zid", _id);
0702: }
0703: }
0704:
0705: public final String getUuid() {
0706: if (_uuid == null) {
0707: final Execution exec = Executions.getCurrent();
0708: _uuid = exec == null ? ComponentsCtrl.ANONYMOUS_ID
0709: : nextUuid(exec.getDesktop());
0710: if (_id == null || (this instanceof RawId))
0711: _id = _uuid;
0712: }
0713: return _uuid;
0714: }
0715:
0716: public final IdSpace getSpaceOwner() {
0717: Component p = this ;
0718: do {
0719: if (p instanceof IdSpace)
0720: return (IdSpace) p;
0721: } while ((p = p.getParent()) != null);
0722: return _page;
0723: }
0724:
0725: public Component getFellow(String compId) {
0726: if (this instanceof IdSpace) {
0727: final Component comp = (Component) _spaceInfo.fellows
0728: .get(compId);
0729: if (comp == null)
0730: if (compId != null && ComponentsCtrl.isAutoId(compId))
0731: throw new ComponentNotFoundException(
0732: MZk.AUTO_ID_NOT_LOCATABLE, compId);
0733: else
0734: throw new ComponentNotFoundException(
0735: "Fellow component not found: " + compId);
0736: return comp;
0737: }
0738:
0739: final IdSpace idspace = getSpaceOwner();
0740: if (idspace == null)
0741: throw new ComponentNotFoundException(
0742: "This component doesn't belong to any ID space: "
0743: + this );
0744: return idspace.getFellow(compId);
0745: }
0746:
0747: public Component getFellowIfAny(String compId) {
0748: if (this instanceof IdSpace)
0749: return (Component) _spaceInfo.fellows.get(compId);
0750:
0751: final IdSpace idspace = getSpaceOwner();
0752: return idspace == null ? null : idspace.getFellowIfAny(compId);
0753: }
0754:
0755: public Component getNextSibling() {
0756: return _next;
0757: }
0758:
0759: public Component getPreviousSibling() {
0760: return _prev;
0761: }
0762:
0763: public Component getFirstChild() {
0764: return _first;
0765: }
0766:
0767: public Component getLastChild() {
0768: return _last;
0769: }
0770:
0771: public Map getAttributes(int scope) {
0772: switch (scope) {
0773: case SPACE_SCOPE:
0774: if (this instanceof IdSpace)
0775: return _spaceInfo.attrs;
0776: final IdSpace idspace = getSpaceOwner();
0777: return idspace instanceof Page ? ((Page) idspace)
0778: .getAttributes()
0779: : idspace == null ? Collections.EMPTY_MAP
0780: : ((Component) idspace)
0781: .getAttributes(SPACE_SCOPE);
0782: case PAGE_SCOPE:
0783: return _page != null ? _page.getAttributes()
0784: : Collections.EMPTY_MAP;
0785: case DESKTOP_SCOPE:
0786: return _page != null ? _page.getDesktop().getAttributes()
0787: : Collections.EMPTY_MAP;
0788: case SESSION_SCOPE:
0789: return _page != null ? _page.getDesktop().getSession()
0790: .getAttributes() : Collections.EMPTY_MAP;
0791: case APPLICATION_SCOPE:
0792: return _page != null ? _page.getDesktop().getWebApp()
0793: .getAttributes() : Collections.EMPTY_MAP;
0794: case COMPONENT_SCOPE:
0795: return _attrs;
0796: case REQUEST_SCOPE:
0797: final Execution exec = getExecution();
0798: if (exec != null)
0799: return exec.getAttributes();
0800: //fall thru
0801: default:
0802: return Collections.EMPTY_MAP;
0803: }
0804: }
0805:
0806: private final Execution getExecution() {
0807: return _page != null ? _page.getDesktop().getExecution()
0808: : Executions.getCurrent();
0809: }
0810:
0811: public Object getAttribute(String name, int scope) {
0812: return getAttributes(scope).get(name);
0813: }
0814:
0815: public Object setAttribute(String name, Object value, int scope) {
0816: if (value != null) {
0817: final Map attrs = getAttributes(scope);
0818: if (attrs == Collections.EMPTY_MAP)
0819: throw new IllegalStateException("This component, "
0820: + this + ", doesn't belong to the "
0821: + Components.scopeToString(scope) + " scope");
0822: return attrs.put(name, value);
0823: } else {
0824: return removeAttribute(name, scope);
0825: }
0826: }
0827:
0828: public Object removeAttribute(String name, int scope) {
0829: final Map attrs = getAttributes(scope);
0830: if (attrs == Collections.EMPTY_MAP)
0831: throw new IllegalStateException(
0832: "This component doesn't belong to any ID space: "
0833: + this );
0834: return attrs.remove(name);
0835: }
0836:
0837: public final Map getAttributes() {
0838: return _attrs;
0839: }
0840:
0841: public final Object getAttribute(String name) {
0842: return _attrs.get(name);
0843: }
0844:
0845: public final Object setAttribute(String name, Object value) {
0846: return value != null ? _attrs.put(name, value) : _attrs
0847: .remove(name);
0848: }
0849:
0850: public final Object removeAttribute(String name) {
0851: return _attrs.remove(name);
0852: }
0853:
0854: public void setVariable(String name, Object val, boolean local) {
0855: getNamespace().setVariable(name, val, local);
0856: }
0857:
0858: public boolean containsVariable(String name, boolean local) {
0859: return getNamespace().containsVariable(name, local);
0860: }
0861:
0862: public Object getVariable(String name, boolean local) {
0863: return getNamespace().getVariable(name, local);
0864: }
0865:
0866: public void unsetVariable(String name, boolean local) {
0867: getNamespace().unsetVariable(name, local);
0868: }
0869:
0870: public Component getParent() {
0871: return _parent;
0872: }
0873:
0874: public void setParent(Component parent) {
0875: if (_parent == parent)
0876: return; //nothing changed
0877:
0878: checkParentChild(parent, this );
0879:
0880: final boolean idSpaceChanged = parent != null ? parent
0881: .getSpaceOwner() != (_parent != null ? _parent
0882: .getSpaceOwner() : _page) : _page != null;
0883:
0884: if (idSpaceChanged)
0885: removeFromIdSpacesDown(this );
0886:
0887: //call removeChild and clear _parent
0888: final AbstractComponent op = _parent;
0889: if (op != null) {
0890: if (!op.inRemoving(this )) {
0891: op.markRemoving(this , true);
0892: try {
0893: op.removeChild(this ); //spec: call back removeChild
0894: } finally {
0895: op.markRemoving(this , false);
0896: }
0897: }
0898: _parent = null; //op.removeChild assumes _parent not changed yet
0899: } else {
0900: if (_page != null)
0901: ((PageCtrl) _page).removeRoot(this ); //Not depends on uuid
0902: }
0903:
0904: //call insertBefore and set _parent
0905: if (parent != null) {
0906: final AbstractComponent np = (AbstractComponent) parent;
0907: if (!np.inAdding(this )) {
0908: np.markAdding(this , true);
0909: try {
0910: np.insertBefore(this , null); //spec: call back inserBefore
0911: } finally {
0912: np.markAdding(this , false);
0913: }
0914: }
0915: _parent = np; //np.insertBefore assumes _parent not changed yet
0916: } //if parent == null, assume no page at all (so no addRoot)
0917:
0918: //correct _page
0919: final Page newpg = _parent != null ? _parent.getPage() : null;
0920: addMoved(op, _page, newpg); //Not depends on UUID
0921: setPage0(newpg); //UUID might be changed here
0922:
0923: if (_spaceInfo != null) //ID space owner
0924: _spaceInfo.ns.setParent(_parent != null ? _parent
0925: .getNamespace() : null);
0926: if (idSpaceChanged)
0927: addToIdSpacesDown(this ); //called after setPage
0928: }
0929:
0930: /** Returns whether the child is being removed.
0931: */
0932: private boolean inRemoving(Component child) {
0933: return _rming != null && _rming.contains(child);
0934: }
0935:
0936: /** Sets if the child is being removed.
0937: */
0938: private void markRemoving(Component child, boolean set) {
0939: if (set) {
0940: if (_rming == null)
0941: _rming = new HashSet(2);
0942: _rming.add(child);
0943: } else {
0944: if (_rming != null && _rming.remove(child)
0945: && _rming.isEmpty())
0946: _rming = null;
0947: }
0948: }
0949:
0950: /** Returns whether the child is being added.
0951: */
0952: private boolean inAdding(Component child) {
0953: return _adding != null && _adding.contains(child);
0954: }
0955:
0956: /** Sets if the child is being added.
0957: */
0958: private void markAdding(Component child, boolean set) {
0959: if (set) {
0960: if (_adding == null)
0961: _adding = new HashSet(2);
0962: _adding.add(child);
0963: } else {
0964: if (_adding != null && _adding.remove(child)
0965: && _adding.isEmpty())
0966: _adding = null;
0967: }
0968: }
0969:
0970: /**
0971: * @param parent the parent (will-be). It may be null.
0972: * @param child the child (will-be). It cannot be null.
0973: */
0974: private static void checkParentChild(Component parent,
0975: Component child) throws UiException {
0976: if (parent != null) {
0977: if (((AbstractComponent) parent).inAdding(child))
0978: return; //check only once
0979:
0980: if (Components.isAncestor(child, parent))
0981: throw new UiException(
0982: "A child cannot be a parent of its ancestor: "
0983: + child);
0984: if (!parent.isChildable())
0985: throw new UiException(parent
0986: + " doesn't allow any child");
0987:
0988: final Page parentpg = parent.getPage(), childpg = child
0989: .getPage();
0990: if (parentpg != null && childpg != null
0991: && parentpg.getDesktop() != childpg.getDesktop())
0992: throw new UiException(
0993: "The parent and child must be in the same desktop: "
0994: + parent);
0995:
0996: final Component oldparent = child.getParent();
0997: if (parent.getSpaceOwner() != (oldparent != null ? oldparent
0998: .getSpaceOwner()
0999: : childpg))
1000: checkIdSpacesDown(child, parent);
1001: } else {
1002: final Page childpg = child.getPage();
1003: if (childpg != null)
1004: checkDetach(childpg);
1005: }
1006: }
1007:
1008: public boolean insertBefore(Component newChild, Component refChild) {
1009: checkParentChild(this , newChild);
1010:
1011: if (refChild != null && refChild.getParent() != this )
1012: refChild = null;
1013:
1014: final AbstractComponent nc = (AbstractComponent) newChild;
1015: final boolean moved = nc._parent == this ; //moved in the same parent
1016: if (moved) {
1017: if (nc._next == refChild)
1018: return false; //nothing changed
1019: nc.addMoved(this , _page, _page);
1020:
1021: //detach from original place
1022: setNext(nc._prev, nc._next);
1023: setPrev(nc._next, nc._prev);
1024: } else { //new added
1025: //Note: call setParent to detach nc from old parent, if any,
1026: //before maintaining nc's _next, _prev...
1027: if (!inAdding(nc)) {
1028: markAdding(nc, true);
1029: try {
1030: nc.setParent(this ); //spec: callback setParent
1031: } finally {
1032: markAdding(nc, false);
1033: }
1034: } else {
1035: nc._parent = this ;
1036: //Set it since deriving class might assume parent is correct
1037: //after insertBefore. For example, Tabs.insertBefore().
1038: //
1039: //However, we don't call setPage0 and other here,
1040: //since the codes will become too complex.
1041: //In other words, when super.insertBefore() returns in a
1042: //deriving class, _parent is correct but _page may or may not
1043: }
1044: }
1045:
1046: if (refChild != null) {
1047: final AbstractComponent ref = (AbstractComponent) refChild;
1048: setNext(nc, ref);
1049: setPrev(nc, ref._prev);
1050: setNext(ref._prev, nc);
1051: setPrev(ref, nc);
1052: } else {
1053: if (_last == null) {
1054: _first = _last = nc;
1055: nc._next = nc._prev = null;
1056: } else {
1057: _last._next = nc;
1058: nc._prev = _last;
1059: nc._next = null;
1060: _last = nc;
1061: }
1062: }
1063:
1064: ++_modCntChd;
1065: if (!moved) { //new added
1066: ++_nChild;
1067: onChildAdded(nc);
1068: }
1069: return true;
1070: }
1071:
1072: private final void setNext(AbstractComponent comp,
1073: AbstractComponent next) {
1074: if (comp != null)
1075: comp._next = next;
1076: else
1077: _first = next;
1078: }
1079:
1080: private final void setPrev(AbstractComponent comp,
1081: AbstractComponent prev) {
1082: if (comp != null)
1083: comp._prev = prev;
1084: else
1085: _last = prev;
1086: }
1087:
1088: /** Appends a child to the end of all children.
1089: * It calls {@link #insertBefore} with refChild to be null.
1090: * Derives cannot override this method, and they shall override
1091: * {@link #insertBefore} instead.
1092: */
1093: public final boolean appendChild(Component child) { //Yes, final; see below
1094: return insertBefore(child, null); //NOTE: we must go thru insertBefore
1095: //such that deriving is easy to override
1096: }
1097:
1098: public boolean removeChild(Component child) {
1099: if (child.getParent() != this )
1100: return false; //nothing to do
1101:
1102: final AbstractComponent oc = (AbstractComponent) child;
1103: setNext(oc._prev, oc._next);
1104: setPrev(oc._next, oc._prev);
1105: oc._next = oc._prev = null;
1106:
1107: if (!inRemoving(oc)) {
1108: markRemoving(oc, true);
1109: try {
1110: oc.setParent(null); //spec: call back setParent
1111: } finally {
1112: markRemoving(oc, false);
1113: }
1114: } else {
1115: oc._parent = null;
1116: //Correct it since deriving class might assume parent is
1117: //correct after insertBefore() returns.
1118: //refer to insertBefore for more info.
1119: }
1120:
1121: ++_modCntChd;
1122: --_nChild;
1123: onChildRemoved(child);
1124: return true;
1125: }
1126:
1127: /** Default: return true (allows to have children).
1128: */
1129: public boolean isChildable() {
1130: return true;
1131: }
1132:
1133: public List getChildren() {
1134: return _apiChildren;
1135: }
1136:
1137: /** Returns the root of the specified component.
1138: */
1139: public Component getRoot() {
1140: for (Component comp = this ;;) {
1141: final Component parent = comp.getParent();
1142: if (parent == null)
1143: return comp;
1144: comp = parent;
1145: }
1146: }
1147:
1148: public boolean isVisible() {
1149: return _visible;
1150: }
1151:
1152: public boolean setVisible(boolean visible) {
1153: final boolean old = _visible;
1154: if (old != visible) {
1155: _visible = visible;
1156: smartUpdate("visibility", _visible);
1157: }
1158: return old;
1159: }
1160:
1161: public void invalidate() {
1162: if (_page != null)
1163: getThisUiEngine().addInvalidate(this );
1164: }
1165:
1166: public void response(String key, AuResponse response) {
1167: //if response not depend on this component, it must be generated
1168: if (_page != null) {
1169: getThisUiEngine().addResponse(key, response);
1170: } else if (response.getDepends() != this ) {
1171: final Execution exec = Executions.getCurrent();
1172: if (exec != null)
1173: ((WebAppCtrl) exec.getDesktop().getWebApp())
1174: .getUiEngine().addResponse(key, response);
1175: }
1176: }
1177:
1178: public void smartUpdate(String attr, String value) {
1179: if (_page != null)
1180: getThisUiEngine().addSmartUpdate(this , attr, value);
1181: }
1182:
1183: /** Smart-updates a property with a deferred value.
1184: * A deferred value is used to encapsulate a value that shall be retrieved
1185: * only in the rendering phase.
1186: *
1187: * @since 3.0.1
1188: */
1189: public void smartUpdateDeferred(String attr, DeferredValue value) {
1190: if (_page != null)
1191: getThisUiEngine().addSmartUpdate(this , attr, value);
1192: }
1193:
1194: /** A special smart-update that update a value in int.
1195: * <p>It will invoke {@link #smartUpdate(String,String)} to update
1196: * the attribute eventually.
1197: */
1198: public void smartUpdate(String attr, int value) {
1199: smartUpdate(attr, Integer.toString(value));
1200: }
1201:
1202: /** A special smart-update that update a value in boolean.
1203: * <p>It will invoke {@link #smartUpdate(String,String)} to update
1204: * the attribute eventually.
1205: */
1206: public void smartUpdate(String attr, boolean value) {
1207: smartUpdate(attr, Boolean.toString(value));
1208: }
1209:
1210: public void detach() {
1211: if (getParent() != null)
1212: setParent(null);
1213: else
1214: setPage(null);
1215: }
1216:
1217: /** Default: does nothing.
1218: * @see Component#onChildAdded
1219: */
1220: public void onChildAdded(Component child) {
1221: }
1222:
1223: /** Default: does nothing.
1224: * @see Component#onChildRemoved
1225: */
1226: public void onChildRemoved(Component child) {
1227: }
1228:
1229: /** Default: handles special event listeners.
1230: * @see Component#onPageAttached
1231: * @since 3.0.0
1232: */
1233: public void onPageAttached(Page newpage, Page oldpage) {
1234: if (oldpage == null) //new added
1235: onListenerChanged(newpage.getDesktop(), true);
1236: }
1237:
1238: /** Default: handles special event listeners.
1239: * @see Component#onPageDetached
1240: * @since 3.0.0
1241: */
1242: public void onPageDetached(Page page) {
1243: onListenerChanged(page.getDesktop(), false);
1244: }
1245:
1246: /** Default: null (no propagation at all).
1247: */
1248: public Component getPropagatee(String evtnm) {
1249: return null;
1250: }
1251:
1252: /** Returns the mold used to render this component.
1253: * Default: "default"
1254: */
1255: public final String getMold() {
1256: return _mold;
1257: }
1258:
1259: public void setMold(String mold) {
1260: if (mold == null || mold.length() == 0)
1261: mold = "default";
1262: if (!Objects.equals(_mold, mold)) {
1263: if (!_def.hasMold(mold))
1264: throw new UiException("Unknown mold: " + mold
1265: + ", while allowed include "
1266: + _def.getMoldNames());
1267: _mold = mold;
1268: invalidate();
1269: }
1270: }
1271:
1272: //-- in the redrawing phase --//
1273: /** Redraws this component.
1274: * This method implements the mold mechanism.
1275: * <ol>
1276: * <li>It first invokes {@link #getMoldURI} to retrieve the mold
1277: * to redraw. The mold is either an URI (String) or a
1278: * {@link ComponentRenderer} instance.
1279: * <li>If URI, it invokes {@link Execution#include} to generate
1280: * the output.</li>
1281: * <li>If a {@link ComponentRenderer} instance, {@link ComponentRenderer#render}
1282: * is called to generate the output.</li>
1283: * </ul>
1284: */
1285: public void redraw(Writer out) throws IOException {
1286: final Object mold = getMoldURI();
1287: if (mold instanceof ComponentRenderer) {
1288: ((ComponentRenderer) mold).render(this , out != null ? out
1289: : ZkFns.getCurrentOut());
1290: } else {
1291: final Map attrs = new HashMap(2);
1292: attrs.put("self", this );
1293: getExecution().include(out, (String) mold, attrs,
1294: Execution.PASS_THRU_ATTR);
1295: }
1296: }
1297:
1298: /* Default: does nothing.
1299: */
1300: public void onDrawNewChild(Component child, StringBuffer out)
1301: throws IOException {
1302: }
1303:
1304: /** Returns whether to send back the request of the specified event
1305: * immediately -- i.e., non-deferrable.
1306: * Returns true if you want the component (on the server)
1307: * to process the event immediately.
1308: *
1309: * <p>Default: return true if any non-deferable event listener of
1310: * the specified event is found. In other words, it returns
1311: * {@link Events#isListened} with asap = true.
1312: *
1313: * <p>This method is moved from {@link HtmlBasedComponent} to
1314: * {@link AbstractComponent} since 3.0.0.
1315: *
1316: * @param evtnm the event name, such as onClick
1317: * @since 3.0.0
1318: */
1319: protected boolean isAsapRequired(String evtnm) {
1320: return Events.isListened(this , evtnm, true);
1321: }
1322:
1323: /** Appends an attribute for the specified event name, say, onChange,
1324: * if a non-deferrable listener is registered.
1325: * The format of the generated attribute is as follows:
1326: * <code>onChange="true"</code>.
1327: *
1328: * <p>This method is moved from {@link HtmlBasedComponent} to
1329: * {@link AbstractComponent} since 3.0.0.
1330: *
1331: * @param sb the string buffer to hold the HTML attribute. If null and
1332: * {@link #isAsapRequired} is true, a string buffer is created and returned.
1333: * @param evtnm the event name, such as onClick
1334: * @return the string buffer. If sb is null and {@link #isAsapRequired}
1335: * returns false, null is returned.
1336: * If the caller passed non-null sb, the returned value must be the same
1337: * as sb (so it usually ignores the returned value).
1338: * @since 3.0.0
1339: */
1340: protected StringBuffer appendAsapAttr(StringBuffer sb, String evtnm) {
1341: if (isAsapRequired(evtnm)) {
1342: if (sb == null)
1343: sb = new StringBuffer(80);
1344: HTMLs.appendAttribute(sb, getAttrOfEvent(evtnm), true);
1345: }
1346: return sb;
1347: }
1348:
1349: private static String getAttrOfEvent(String evtnm) {
1350: return Events.ON_CLICK.equals(evtnm) ? "z.lfclk"
1351: : Events.ON_RIGHT_CLICK.equals(evtnm) ? "z.rtclk"
1352: : Events.ON_DOUBLE_CLICK.equals(evtnm) ? "z.dbclk"
1353: : "z." + evtnm;
1354: }
1355:
1356: public boolean addEventListener(String evtnm, EventListener listener) {
1357: if (evtnm == null || listener == null)
1358: throw new IllegalArgumentException("null");
1359: if (!Events.isValid(evtnm))
1360: throw new IllegalArgumentException("Invalid event name: "
1361: + evtnm);
1362:
1363: final boolean asap = isAsapRequired(evtnm);
1364:
1365: if (_listeners == null)
1366: _listeners = new HashMap(8);
1367:
1368: List l = (List) _listeners.get(evtnm);
1369: if (l != null) {
1370: for (Iterator it = l.iterator(); it.hasNext();) {
1371: final EventListener li = (EventListener) it.next();
1372: if (listener.equals(li))
1373: return false;
1374: }
1375: } else {
1376: _listeners.put(evtnm, l = new LinkedList());
1377: }
1378: l.add(listener);
1379:
1380: final Desktop desktop = getDesktop();
1381: if (desktop != null) {
1382: if (Events.ON_CLIENT_INFO.equals(evtnm))
1383: response("clientInfo", new AuClientInfo(desktop));
1384: if (Events.ON_PIGGYBACK.equals(evtnm))
1385: ((DesktopCtrl) desktop).onPiggybackListened(this , true);
1386:
1387: if (!asap && isAsapRequired(evtnm))
1388: smartUpdate(getAttrOfEvent(evtnm), "true");
1389: }
1390: return true;
1391: }
1392:
1393: public boolean removeEventListener(String evtnm,
1394: EventListener listener) {
1395: if (evtnm == null || listener == null)
1396: throw new IllegalArgumentException("null");
1397:
1398: if (_listeners != null) {
1399: final boolean asap = isAsapRequired(evtnm);
1400: final List l = (List) _listeners.get(evtnm);
1401: if (l != null) {
1402: for (Iterator it = l.iterator(); it.hasNext();) {
1403: final EventListener li = (EventListener) it.next();
1404: if (listener.equals(li)) {
1405: if (l.size() == 1)
1406: _listeners.remove(evtnm);
1407: else
1408: it.remove();
1409:
1410: final Desktop desktop = getDesktop();
1411: if (desktop != null) {
1412: onListenerChanged(desktop, false);
1413:
1414: if (asap && !isAsapRequired(evtnm))
1415: smartUpdate(getAttrOfEvent(evtnm), null);
1416: }
1417: return true;
1418: }
1419: }
1420: }
1421: }
1422: return false;
1423: }
1424:
1425: public boolean addForward(String orgEvent, Component target,
1426: String targetEvent) {
1427: return addForward0(orgEvent, target, targetEvent);
1428: }
1429:
1430: public boolean addForward(String orgEvent, String targetPath,
1431: String targetEvent) {
1432: return addForward0(orgEvent, targetPath, targetEvent);
1433: }
1434:
1435: /**
1436: * @param target the target. It is either a component, or a string,
1437: * which is used internal for implementing {@link #writeObject}
1438: */
1439: private boolean addForward0(String orgEvent, Object target,
1440: String targetEvent) {
1441: if (orgEvent == null)
1442: orgEvent = "onClick";
1443: else if (!Events.isValid(orgEvent))
1444: throw new IllegalArgumentException("Illegal event name: "
1445: + orgEvent);
1446: if (targetEvent == null)
1447: targetEvent = orgEvent;
1448: else if (!Events.isValid(targetEvent))
1449: throw new IllegalArgumentException("Illegal event name: "
1450: + targetEvent);
1451:
1452: if (_forwards == null)
1453: _forwards = new HashMap(4);
1454:
1455: Object[] info = (Object[]) _forwards.get(orgEvent);
1456: final List fwds;
1457: if (info != null) {
1458: fwds = (List) info[1];
1459: for (Iterator it = fwds.iterator(); it.hasNext();) {
1460: final Object[] fwd = (Object[]) it.next();
1461: if (Objects.equals(fwd[0], target)
1462: && Objects.equals(fwd[1], targetEvent)) //found
1463: return false;
1464: }
1465: } else {
1466: final ForwardListener listener = new ForwardListener(
1467: orgEvent);
1468: addEventListener(orgEvent, listener);
1469: info = new Object[] { listener, fwds = new LinkedList() };
1470: _forwards.put(orgEvent, info);
1471: }
1472:
1473: fwds.add(new Object[] { target, targetEvent });
1474: return true;
1475: }
1476:
1477: public boolean removeForward(String orgEvent, Component target,
1478: String targetEvent) {
1479: return removeForward0(orgEvent, target, targetEvent);
1480: }
1481:
1482: public boolean removeForward(String orgEvent, String targetPath,
1483: String targetEvent) {
1484: return removeForward0(orgEvent, targetPath, targetEvent);
1485: }
1486:
1487: private boolean removeForward0(String orgEvent, Object target,
1488: String targetEvent) {
1489: if (_forwards != null) {
1490: final Object[] info = (Object[]) _forwards.get(orgEvent);
1491: if (info != null) {
1492: final List fwds = (List) info[1];
1493: for (Iterator it = fwds.iterator(); it.hasNext();) {
1494: final Object[] fwd = (Object[]) it.next();
1495: if (Objects.equals(fwd[0], target)
1496: && Objects.equals(fwd[1], targetEvent)) { //found
1497: it.remove(); //remove it
1498:
1499: if (fwds.isEmpty()) { //no more event
1500: _forwards.remove(orgEvent);
1501: removeEventListener(orgEvent,
1502: (EventListener) info[0]);
1503: }
1504: return true;
1505: }
1506: }
1507: }
1508: }
1509: return false;
1510: }
1511:
1512: public Namespace getNamespace() {
1513: if (this instanceof IdSpace)
1514: return _spaceInfo.ns;
1515:
1516: final IdSpace idspace = getSpaceOwner();
1517: return idspace instanceof Page ? ((Page) idspace)
1518: .getNamespace() : idspace == null ? null
1519: : ((Component) idspace).getNamespace();
1520: }
1521:
1522: public boolean isListenerAvailable(String evtnm, boolean asap) {
1523: if (_listeners != null) {
1524: final List l = (List) _listeners.get(evtnm);
1525: if (l != null) {
1526: if (!asap)
1527: return !l.isEmpty();
1528:
1529: for (Iterator it = l.iterator(); it.hasNext();) {
1530: final EventListener li = (EventListener) it.next();
1531: if (!(li instanceof Deferrable)
1532: || !(((Deferrable) li).isDeferrable()))
1533: return true;
1534: }
1535: }
1536: }
1537: return false;
1538: }
1539:
1540: public Iterator getListenerIterator(String evtnm) {
1541: if (_listeners != null) {
1542: final List l = (List) _listeners.get(evtnm);
1543: if (l != null)
1544: return new ListenerIterator(l);
1545: }
1546: return CollectionsX.EMPTY_ITERATOR;
1547: }
1548:
1549: public void applyProperties() {
1550: _def.applyProperties(this );
1551: }
1552:
1553: public ComponentDefinition getDefinition() {
1554: return _def;
1555: }
1556:
1557: //-- ComponentCtrl --//
1558: public void setComponentDefinition(ComponentDefinition compdef) {
1559: if (compdef == null)
1560: throw new IllegalArgumentException("null");
1561: if (!compdef.isInstance(this ))
1562: throw new IllegalArgumentException("Incompatible "
1563: + compdef + " for " + this );
1564: _def = compdef;
1565: }
1566:
1567: public ZScript getEventHandler(String evtnm) {
1568: final EventHandler evthd = _evthds != null ? _evthds.get(this ,
1569: evtnm) : null;
1570: return evthd != null ? evthd.getZScript() : null;
1571: }
1572:
1573: public void addSharedEventHandlerMap(EventHandlerMap evthds) {
1574: if (evthds != null && !evthds.isEmpty()) {
1575: unshareEventHandlerMap(false);
1576: if (_evthds == null) {
1577: _evthds = evthds;
1578: _evthdsShared = true;
1579: } else {
1580: _evthds.addAll(evthds);
1581: }
1582:
1583: final Desktop desktop = getDesktop();
1584: if (desktop != null)
1585: onListenerChanged(desktop, true);
1586: }
1587: }
1588:
1589: public Set getEventHandlerNames() {
1590: return _evthds != null ? _evthds.getEventNames()
1591: : Collections.EMPTY_SET;
1592: }
1593:
1594: private void onListenerChanged(Desktop desktop, boolean listen) {
1595: if (listen) {
1596: if (Events.isListened(this , Events.ON_CLIENT_INFO, false)) //asap+deferrable
1597: response("clientInfo", new AuClientInfo(desktop));
1598: //We always fire event not a root, since we don't like to
1599: //check when setParent or setPage is called
1600: if (Events.isListened(this , Events.ON_PIGGYBACK, false))
1601: ((DesktopCtrl) desktop).onPiggybackListened(this , true);
1602: } else {
1603: if (!Events.isListened(this , Events.ON_PIGGYBACK, false))
1604: ((DesktopCtrl) desktop)
1605: .onPiggybackListened(this , false);
1606: }
1607: }
1608:
1609: public void addEventHandler(String name, EventHandler evthd) {
1610: if (name == null || evthd == null)
1611: throw new IllegalArgumentException(
1612: "name and evthd required");
1613:
1614: unshareEventHandlerMap(true);
1615: _evthds.add(name, evthd);
1616: }
1617:
1618: /** Clones the shared event handlers, if shared.
1619: * @param autocreate whether to create an event handler map if not available.
1620: */
1621: private void unshareEventHandlerMap(boolean autocreate) {
1622: if (_evthdsShared) {
1623: _evthds = (EventHandlerMap) _evthds.clone();
1624: _evthdsShared = false;
1625: } else if (autocreate && _evthds == null) {
1626: _evthds = new EventHandlerMap();
1627: }
1628: }
1629:
1630: public Annotation getAnnotation(String annotName) {
1631: return _annots != null ? _annots.getAnnotation(annotName)
1632: : null;
1633: }
1634:
1635: public Annotation getAnnotation(String propName, String annotName) {
1636: return _annots != null ? _annots.getAnnotation(propName,
1637: annotName) : null;
1638: }
1639:
1640: public Collection getAnnotations() {
1641: return _annots != null ? _annots.getAnnotations()
1642: : Collections.EMPTY_LIST;
1643: }
1644:
1645: public Collection getAnnotations(String propName) {
1646: return _annots != null ? _annots.getAnnotations(propName)
1647: : Collections.EMPTY_LIST;
1648: }
1649:
1650: public List getAnnotatedPropertiesBy(String annotName) {
1651: return _annots != null ? _annots
1652: .getAnnotatedPropertiesBy(annotName)
1653: : Collections.EMPTY_LIST;
1654: }
1655:
1656: public List getAnnotatedProperties() {
1657: return _annots != null ? _annots.getAnnotatedProperties()
1658: : Collections.EMPTY_LIST;
1659: }
1660:
1661: public void addSharedAnnotationMap(AnnotationMap annots) {
1662: if (annots != null && !annots.isEmpty()) {
1663: unshareAnnotationMap(false);
1664: if (_annots == null) {
1665: _annots = annots;
1666: _annotsShared = true;
1667: } else {
1668: _annots.addAll(annots);
1669: }
1670: }
1671: }
1672:
1673: public void addAnnotation(String annotName, Map annotAttrs) {
1674: unshareAnnotationMap(true);
1675: _annots.addAnnotation(annotName, annotAttrs);
1676: }
1677:
1678: public void addAnnotation(String propName, String annotName,
1679: Map annotAttrs) {
1680: unshareAnnotationMap(true);
1681: _annots.addAnnotation(propName, annotName, annotAttrs);
1682: }
1683:
1684: /** Clones the shared annotations, if shared.
1685: * @param autocreate whether to create an annotation map if not available.
1686: */
1687: private void unshareAnnotationMap(boolean autocreate) {
1688: if (_annotsShared) {
1689: _annots = (AnnotationMap) _annots.clone();
1690: _annotsShared = false;
1691: } else if (autocreate && _annots == null) {
1692: _annots = new AnnotationMap();
1693: }
1694: }
1695:
1696: public void sessionWillPassivate(Page page) {
1697: //nothing to do
1698: }
1699:
1700: public void sessionDidActivate(Page page) {
1701: sessionDidActivate0(page, this , true);
1702: }
1703:
1704: /**
1705: * @param pageLevelIdSpace whether this component's ID space is
1706: * at the page level.
1707: */
1708: private static void sessionDidActivate0(Page page,
1709: AbstractComponent comp, boolean pageLevelIdSpace) {
1710: comp._page = page;
1711:
1712: //Note: we need only to fix the first-level spaceInfo.
1713: //Others are handled by readObject
1714: if (pageLevelIdSpace && comp._spaceInfo != null) {
1715: pageLevelIdSpace = false;
1716: comp._spaceInfo.ns.setParent(page.getNamespace());
1717: }
1718:
1719: for (AbstractComponent p = comp._first; p != null; p = p._next) {
1720: sessionDidActivate0(page, p, pageLevelIdSpace); //recursive
1721: }
1722: }
1723:
1724: /** Returns the extra controls that tell ZK how to handle this component
1725: * specially.
1726: * It is used only by component developers.
1727: *
1728: * <p>It is simpler to override {@link #newExtraCtrl} instead of this.
1729: * By use of {@link #newExtraCtrl}, you don't need to care of
1730: * cloning and serialization.
1731: *
1732: * <p>Default: return the object being created by {@link #newExtraCtrl},
1733: * if any.
1734: *
1735: * @see ComponentCtrl#getExtraCtrl
1736: */
1737: public Object getExtraCtrl() {
1738: if (_xtrl == null)
1739: _xtrl = newExtraCtrl();
1740: //3.0.3: create as late as possible so component has a chance
1741: //to customize which object to instantiate
1742: return _xtrl;
1743: }
1744:
1745: /** Used by {@link #getExtraCtrl} to create a client control.
1746: * It is used only by component developers.
1747: *
1748: * <p>Default: return null.
1749: *
1750: * <p>To provide extra controls, it is simpler to override this method
1751: * instead of {@link #getExtraCtrl}.
1752: * By use of {@link #newExtraCtrl}, you don't need to care of
1753: * cloning and serialization.
1754: */
1755: protected Object newExtraCtrl() {
1756: return null;
1757: }
1758:
1759: /** Notifies that an {@link WrongValueException} instance is thrown,
1760: * and {@link WrongValueException#getComponent} is this component.
1761: * It is a callback and the component can store the error message,
1762: * show up the custom information, or even 'eat' the exception.
1763: *
1764: * <p>Default: does nothing but returns ex.
1765: *
1766: * @param ex the exception being thrown (never null)
1767: * @return the exception to throw, or null to ignore the exception
1768: * In most cases, just return ex
1769: * @since 2.4.0
1770: */
1771: public WrongValueException onWrongValue(WrongValueException ex) {
1772: return ex;
1773: }
1774:
1775: //-- Object --//
1776: public String toString() {
1777: final String clsnm = getClass().getName();
1778: final int j = clsnm.lastIndexOf('.');
1779: return "<"
1780: + clsnm.substring(j + 1)
1781: + ' '
1782: + (_id == null || ComponentsCtrl.isAutoId(_id) ? _uuid
1783: : _id) + '>';
1784: }
1785:
1786: public final boolean equals(Object o) { //no more override
1787: return this == o;
1788: }
1789:
1790: /** Holds info shared of the same ID space. */
1791: private static class SpaceInfo {
1792: private Map attrs = new HashMap(8);
1793: //don't create it dynamically because _ip bind it at constructor
1794: private SimpleNamespace ns;
1795: /** A map of ((String id, Component fellow). */
1796: private Map fellows = new HashMap(32);
1797:
1798: private SpaceInfo(Component owner) {
1799: ns = new SimpleNamespace(owner);
1800: init(owner);
1801: }
1802:
1803: private SpaceInfo(Component owner, SimpleNamespace from) {
1804: ns = new SimpleNamespace(owner);
1805: ns.copy(from);
1806: init(owner);
1807: }
1808:
1809: private void init(Component owner) {
1810: ns.setVariable("spaceScope", attrs, true);
1811: ns.setVariable("spaceOwner", owner, true);
1812: }
1813: }
1814:
1815: private class ChildIter implements ListIterator {
1816: private AbstractComponent _p, _lastRet;
1817: private int _j;
1818: private int _modCntSnap;
1819:
1820: private ChildIter(int index) {
1821: if (index < 0 || index > _nChild)
1822: throw new IndexOutOfBoundsException("Index: " + index
1823: + ", Size: " + _nChild);
1824:
1825: if (index < (_nChild >> 1)) {
1826: _p = _first;
1827: for (_j = 0; _j < index; _j++)
1828: _p = _p._next;
1829: } else {
1830: _p = null; //means the end of the list
1831: for (_j = _nChild; _j > index; _j--)
1832: _p = _p != null ? _p._prev : _last;
1833: }
1834:
1835: _modCntSnap = _modCntChd;
1836: }
1837:
1838: public boolean hasNext() {
1839: checkComodification();
1840: return _j < _nChild;
1841: }
1842:
1843: public Object next() {
1844: if (_j >= _nChild)
1845: throw new java.util.NoSuchElementException();
1846: checkComodification();
1847:
1848: _lastRet = _p;
1849: _p = _p._next;
1850: _j++;
1851: return _lastRet;
1852: }
1853:
1854: public boolean hasPrevious() {
1855: checkComodification();
1856: return _j > 0;
1857: }
1858:
1859: public Object previous() {
1860: if (_j <= 0)
1861: throw new java.util.NoSuchElementException();
1862: checkComodification();
1863:
1864: _lastRet = _p = _p != null ? _p._prev : _last;
1865: _j--;
1866: return _lastRet;
1867: }
1868:
1869: private void checkComodification() {
1870: if (_modCntChd != _modCntSnap)
1871: throw new java.util.ConcurrentModificationException();
1872: }
1873:
1874: public int nextIndex() {
1875: return _j;
1876: }
1877:
1878: public int previousIndex() {
1879: return _j - 1;
1880: }
1881:
1882: public void add(Object o) {
1883: final Component newChild = (Component) o;
1884: if (newChild.getParent() == AbstractComponent.this )
1885: throw new UnsupportedOperationException(
1886: "Unable to add component with the same parent: "
1887: + o);
1888: //1. it is confusing to allow adding (with replace)
1889: //2. the code is sophisticated
1890: checkComodification();
1891:
1892: insertBefore(newChild, _p);
1893: ++_j;
1894: _lastRet = null;
1895: //spec: cause remove to throw ex if no next/previous
1896: ++_modCntSnap;
1897: //don't assign _modCntChd directly since deriving class
1898: //might manipulate others in insertBefore
1899: }
1900:
1901: public void remove() {
1902: if (_lastRet == null)
1903: throw new IllegalStateException();
1904: checkComodification();
1905:
1906: removeChild(_lastRet);
1907:
1908: if (_p == _lastRet)
1909: _p = _lastRet._next; //previous was called
1910: else
1911: --_j; //next was called
1912: _lastRet = null;
1913: ++_modCntSnap;
1914: }
1915:
1916: public void set(Object o) {
1917: throw new UnsupportedOperationException();
1918: //Possible to implement this but confusing to developers
1919: //if o has the same parent (since we have to move)
1920: }
1921: }
1922:
1923: //Cloneable//
1924: public Object clone() {
1925: final AbstractComponent clone;
1926: try {
1927: clone = (AbstractComponent) super .clone();
1928: } catch (CloneNotSupportedException e) {
1929: throw new InternalError();
1930: }
1931:
1932: //1. make it not belonging to any page
1933: clone._page = null;
1934: clone._parent = null;
1935:
1936: //1a. clone attributes
1937: clone._attrs = new HashMap(4);
1938: for (Iterator it = _attrs.entrySet().iterator(); it.hasNext();) {
1939: final Map.Entry me = (Map.Entry) it.next();
1940: Object val = me.getValue();
1941: if (val instanceof ComponentCloneListener) {
1942: val = ((ComponentCloneListener) val).clone(clone);
1943: if (val == null)
1944: continue; //don't use it in clone
1945: }
1946: clone._attrs.put(me.getKey(), val);
1947: }
1948:
1949: //1b. clone listeners
1950: if (_listeners != null) {
1951: clone._listeners = new HashMap(4);
1952: for (Iterator it = _listeners.entrySet().iterator(); it
1953: .hasNext();) {
1954: final Map.Entry me = (Map.Entry) it.next();
1955: final List list = new LinkedList();
1956: for (Iterator it2 = ((List) me.getValue()).iterator(); it2
1957: .hasNext();) {
1958: Object val = it2.next();
1959: if (val instanceof ComponentCloneListener) {
1960: val = ((ComponentCloneListener) val)
1961: .clone(clone);
1962: if (val == null)
1963: continue; //don't use it in clone
1964: }
1965: list.add(val);
1966: }
1967: if (!list.isEmpty())
1968: clone._listeners.put(me.getKey(), list);
1969: }
1970: }
1971:
1972: if (!_annotsShared && _annots != null)
1973: clone._annots = (AnnotationMap) _annots.clone();
1974: if (!_evthdsShared && _evthds != null)
1975: clone._evthds = (EventHandlerMap) _evthds.clone();
1976:
1977: //2. clone children (deep cloning)
1978: cloneChildren(clone);
1979: clone.init(true);
1980:
1981: //3. spaceinfo
1982: if (clone._spaceInfo != null) {
1983: clone._spaceInfo = new SpaceInfo(clone, _spaceInfo.ns);
1984: cloneSpaceInfo(clone, this ._spaceInfo);
1985: }
1986:
1987: //4. clone _forwards
1988: if (clone._forwards != null) {
1989: clone._forwards = null;
1990: for (Iterator it = _forwards.entrySet().iterator(); it
1991: .hasNext();) {
1992: final Map.Entry me = (Map.Entry) it.next();
1993: final String orgEvent = (String) me.getKey();
1994:
1995: final Object[] info = (Object[]) me.getValue();
1996: final List fwds = (List) info[1];
1997: for (Iterator e = fwds.iterator(); e.hasNext();) {
1998: final Object[] fwd = (Object[]) e.next();
1999: clone
2000: .addForward0(orgEvent, fwd[0],
2001: (String) fwd[1]);
2002: }
2003: }
2004: }
2005: return clone;
2006: }
2007:
2008: private static final void cloneSpaceInfo(AbstractComponent clone,
2009: SpaceInfo from) {
2010: final SpaceInfo to = clone._spaceInfo;
2011: to.attrs = new HashMap(8);
2012: for (Iterator it = from.attrs.entrySet().iterator(); it
2013: .hasNext();) {
2014: final Map.Entry me = (Map.Entry) it.next();
2015: Object val = me.getValue();
2016: if (val instanceof ComponentCloneListener) {
2017: val = ((ComponentCloneListener) val).clone(clone);
2018: if (val == null)
2019: continue; //don't use it in clone
2020: }
2021: to.attrs.put(me.getKey(), val);
2022: }
2023:
2024: //rebuild ID space by binding itself and all children
2025: clone.bindToIdSpace(clone);
2026: for (AbstractComponent p = clone._first; p != null; p = p._next)
2027: addToIdSpacesDown(p, clone);
2028: }
2029:
2030: private static final void cloneChildren(final AbstractComponent comp) {
2031: AbstractComponent q = null;
2032: for (AbstractComponent p = comp._first; p != null; p = p._next) {
2033: AbstractComponent child = (AbstractComponent) p.clone();
2034: if (q != null)
2035: q._next = child;
2036: else
2037: comp._first = child;
2038: child._prev = q;
2039: q = child;
2040:
2041: child._parent = comp; //correct it
2042: if (child._spaceInfo != null)
2043: child._spaceInfo.ns.setParent(comp.getNamespace());
2044: }
2045: comp._last = q;
2046: }
2047:
2048: //Serializable//
2049: //NOTE: they must be declared as private
2050: private synchronized void writeObject(java.io.ObjectOutputStream s)
2051: throws java.io.IOException {
2052: //No need to unshare since they are stored as an independent copy
2053: //unshareAnnotationMap(false);
2054: //unshareEventHandlerMap(false);
2055:
2056: s.defaultWriteObject();
2057:
2058: //write definition
2059: if (_def == ComponentsCtrl.DUMMY) {
2060: s.writeObject(null);
2061: } else {
2062: LanguageDefinition langdef = _def.getLanguageDefinition();
2063: if (langdef != null) {
2064: s.writeObject(langdef.getName());
2065: s.writeObject(_def.getName());
2066: } else {
2067: s.writeObject(_def);
2068: }
2069: }
2070:
2071: //write children
2072: for (AbstractComponent p = _first; p != null; p = p._next)
2073: s.writeObject(p);
2074: s.writeObject(null);
2075:
2076: //write attrs
2077: willSerialize(_attrs.values());
2078: Serializables.smartWrite(s, _attrs);
2079:
2080: if (_listeners != null)
2081: for (Iterator it = _listeners.entrySet().iterator(); it
2082: .hasNext();) {
2083: final Map.Entry me = (Map.Entry) it.next();
2084: s.writeObject(me.getKey());
2085:
2086: final Collection ls = (Collection) me.getValue();
2087: willSerialize(ls);
2088: Serializables.smartWrite(s, ls);
2089: }
2090: s.writeObject(null);
2091:
2092: //store _spaceInfo
2093: if (this instanceof IdSpace) {
2094: //write _spaceInfo.attrs
2095: willSerialize(_spaceInfo.attrs.values());
2096: Serializables.smartWrite(s, _spaceInfo.attrs);
2097:
2098: //write _spaceInfo.ns (only variables that are not fellows)
2099: for (Iterator it = _spaceInfo.ns.getVariableNames()
2100: .iterator(); it.hasNext();) {
2101: final String nm = (String) it.next();
2102: final Object val = _spaceInfo.ns.getVariable(nm, true);
2103: willSerialize(val); //always called even if not serializable
2104:
2105: if (isVariableSerializable(nm, val)
2106: && (val instanceof java.io.Serializable || val instanceof java.io.Externalizable)) {
2107: s.writeObject(nm);
2108: s.writeObject(val);
2109: }
2110: }
2111: s.writeObject(null); //denote end-of-namespace
2112: }
2113:
2114: //write _forwards
2115: if (_forwards != null) {
2116: for (Iterator it = _forwards.entrySet().iterator(); it
2117: .hasNext();) {
2118: final Map.Entry me = (Map.Entry) it.next();
2119: s.writeObject(me.getKey()); //original event
2120:
2121: final Object[] info = (Object[]) me.getValue();
2122: final List fwds = (List) info[1];
2123: s.writeInt(fwds.size());
2124: for (Iterator e = fwds.iterator(); e.hasNext();) {
2125: final Object[] fwd = (Object[]) e.next();
2126: s.writeObject( //store target as string
2127: fwd[0] instanceof Component ? Components
2128: .componentToPath(
2129: (Component) fwd[0], this )
2130: : fwd[0]);
2131: s.writeObject(fwd[1]); //target event
2132: }
2133: }
2134: }
2135: s.writeObject(null);
2136: }
2137:
2138: private void willSerialize(Collection c) {
2139: if (c != null)
2140: for (Iterator it = c.iterator(); it.hasNext();)
2141: willSerialize(it.next());
2142: }
2143:
2144: private void willSerialize(Object o) {
2145: if (o instanceof ComponentSerializationListener)
2146: ((ComponentSerializationListener) o).willSerialize(this );
2147: }
2148:
2149: private synchronized void readObject(java.io.ObjectInputStream s)
2150: throws java.io.IOException, ClassNotFoundException {
2151: s.defaultReadObject();
2152:
2153: init(false);
2154:
2155: //read definition
2156: Object def = s.readObject();
2157: if (def == null) {
2158: _def = ComponentsCtrl.DUMMY;
2159: } else if (def instanceof String) {
2160: LanguageDefinition langdef = LanguageDefinition
2161: .lookup((String) def);
2162: _def = langdef.getComponentDefinition((String) s
2163: .readObject());
2164: } else {
2165: _def = (ComponentDefinition) def;
2166: }
2167:
2168: //read children
2169: for (AbstractComponent q = null;;) {
2170: final AbstractComponent child = (AbstractComponent) s
2171: .readObject();
2172: if (child == null) {
2173: _last = q;
2174: break; //no more
2175: }
2176: if (q != null)
2177: q._next = child;
2178: else
2179: _first = child;
2180: child._prev = q;
2181: child._parent = this ;
2182: q = child;
2183: }
2184:
2185: //read attrs
2186: Serializables.smartRead(s, _attrs);
2187: didDeserialize(_attrs.values());
2188:
2189: for (;;) {
2190: final String evtnm = (String) s.readObject();
2191: if (evtnm == null)
2192: break; //no more
2193:
2194: if (_listeners == null)
2195: _listeners = new HashMap(4);
2196: final Collection ls = Serializables.smartRead(s,
2197: (Collection) null);
2198: _listeners.put(evtnm, ls);
2199: didDeserialize(ls);
2200: }
2201:
2202: //restore _spaceInfo
2203: if (this instanceof IdSpace) {
2204: _spaceInfo = new SpaceInfo(this );
2205:
2206: //fix child's _spaceInfo's parent
2207: fixSpaceParentOneLevelDown(this , _spaceInfo.ns);
2208:
2209: //read _spaceInfo.attrs
2210: Serializables.smartRead(s, _spaceInfo.attrs);
2211: didDeserialize(_spaceInfo.attrs.values());
2212:
2213: //_spaceInfo.ns
2214: for (;;) {
2215: final String nm = (String) s.readObject();
2216: if (nm == null)
2217: break; //no more
2218:
2219: Object val = s.readObject();
2220: _spaceInfo.ns.setVariable(nm, val, true);
2221: didDeserialize(val);
2222: }
2223:
2224: //restore ID space by binding itself and all children
2225: bindToIdSpace(this );
2226: for (Iterator it = getChildren().iterator(); it.hasNext();)
2227: addToIdSpacesDown((Component) it.next(), this );
2228: }
2229:
2230: //restore _forwards
2231: for (;;) {
2232: final String orgEvent = (String) s.readObject();
2233: if (orgEvent == null)
2234: break;
2235:
2236: int sz = s.readInt();
2237: while (--sz >= 0)
2238: addForward0(orgEvent, s.readObject(), (String) s
2239: .readObject());
2240: //Note: we don't call Components.pathToComponent here
2241: //since the parent doesn't deserialized completely
2242: //Rather, we handle it until the event is received
2243: }
2244: }
2245:
2246: private void didDeserialize(Collection c) {
2247: if (c != null)
2248: for (Iterator it = c.iterator(); it.hasNext();)
2249: didDeserialize(it.next());
2250: }
2251:
2252: private void didDeserialize(Object o) {
2253: if (o instanceof ComponentSerializationListener)
2254: ((ComponentSerializationListener) o).didDeserialize(this );
2255: }
2256:
2257: private static boolean isVariableSerializable(String name,
2258: Object value) {
2259: return !"spaceScope".equals(name) && !"spaceOwner".equals(name)
2260: && !(value instanceof Component);
2261: }
2262:
2263: /** Fixed Namespace's parent of children only one level.
2264: */
2265: private static final void fixSpaceParentOneLevelDown(
2266: Component comp, Namespace nsparent) {
2267: for (Iterator it = comp.getChildren().iterator(); it.hasNext();) {
2268: final AbstractComponent child = (AbstractComponent) it
2269: .next();
2270: //Others are handled by readObject
2271: if (child._spaceInfo != null)
2272: child._spaceInfo.ns.setParent(nsparent);
2273: else
2274: fixSpaceParentOneLevelDown(child, nsparent); //recursive
2275: }
2276: }
2277:
2278: /** Used to forward events (for the forward conditions).
2279: */
2280: private class ForwardListener implements EventListener,
2281: ComponentCloneListener {
2282: //Note: it is not serializable since it is handled by
2283: //AbstractComponent.writeObject
2284:
2285: private final String _orgEvent;
2286:
2287: private ForwardListener(String orgEvent) {
2288: _orgEvent = orgEvent;
2289: }
2290:
2291: public void onEvent(Event event) {
2292: final Object[] info = (Object[]) _forwards.get(_orgEvent);
2293: if (info != null)
2294: for (Iterator it = ((List) info[1]).iterator(); it
2295: .hasNext();) {
2296: final Object[] fwd = (Object[]) it.next();
2297: Component target = fwd[0] instanceof String ? Components
2298: .pathToComponent((String) fwd[0],
2299: AbstractComponent.this )
2300: : (Component) fwd[0];
2301:
2302: if (target == null) {
2303: final IdSpace owner = getSpaceOwner();
2304: if (owner instanceof Component) {
2305: target = (Component) owner;
2306: } else {
2307: //Use the root component instead
2308: for (target = AbstractComponent.this ;;) {
2309: final Component p = target.getParent();
2310: if (p == null)
2311: break;
2312: target = p;
2313: }
2314: }
2315: }
2316:
2317: Events.postEvent(new ForwardEvent((String) fwd[1],
2318: target, event));
2319: }
2320: }
2321:
2322: //ComponentCloneListener//
2323: public Object clone(Component comp) {
2324: return null; //handle by AbstractComponent.clone
2325: }
2326: }
2327: }
|