📄 uivisualizer.java
字号:
/* UiVisualizer.java{{IS_NOTE Purpose: Description: History: Tue Jun 14 10:57:48 2005, Created by tomyeh}}IS_NOTECopyright (C) 2005 Potix Corporation. All Rights Reserved.{{IS_RIGHT This program is distributed under GPL Version 2.0 in the hope that it will be useful, but WITHOUT ANY WARRANTY.}}IS_RIGHT*/package org.zkoss.zk.ui.impl;import java.util.Iterator;import java.util.ListIterator;import java.util.Collection;import java.util.Collections;import java.util.List;import java.util.LinkedList;import java.util.Set;import java.util.LinkedHashSet;import java.util.HashSet;import java.util.Map;import java.util.LinkedHashMap;import java.util.HashMap;import java.util.Arrays;import java.io.StringWriter;import java.io.IOException;import org.zkoss.lang.D;import org.zkoss.lang.Objects;import org.zkoss.util.logging.Log;import org.zkoss.zk.ui.Desktop;import org.zkoss.zk.ui.Page;import org.zkoss.zk.ui.Component;import org.zkoss.zk.ui.Components;import org.zkoss.zk.ui.Execution;import org.zkoss.zk.ui.UiException;import org.zkoss.zk.ui.ext.render.Transparent;import org.zkoss.zk.ui.ext.render.Cropper;import org.zkoss.zk.ui.ext.render.ChildChangedAware;import org.zkoss.zk.ui.ext.render.MultiBranch;import org.zkoss.zk.ui.sys.Visualizer;import org.zkoss.zk.ui.sys.DesktopCtrl;import org.zkoss.zk.ui.sys.PageCtrl;import org.zkoss.zk.ui.sys.ComponentCtrl;import org.zkoss.zk.ui.sys.AbortingReason;import org.zkoss.zk.au.*;/** * An implementation of {@link Visualizer} that works with * {@link UiEngineImpl}. * * @author tomyeh *//*package*/ class UiVisualizer implements Visualizer { private static final Log log = Log.lookup(UiVisualizer.class); /** The first exec info that causes a chain of executions (never null). */ private final UiVisualizer _1stec; /** The associated execution. */ private final Execution _exec; /** A set of invalidated pages. */ private Set _pgInvalid; /** A set of removed pages. */ private Set _pgRemoved; /** A set of invalidated components (Component). */ private final Set _invalidated = new LinkedHashSet(37); /** A map of smart updates (Component comp, Map(String name, TimedValue(comp,name,value))). */ private final Map _smartUpdated = new HashMap(53); //we use TimedValue for better sequence control /** A set of new attached components. */ private final Set _attached = new LinkedHashSet(37); /** A set of moved components (parent changed or page changed). */ private final Set _moved = new LinkedHashSet(37); /** A map of components whose UUID is changed (Component, UUID). */ private Map _idChgd; /** A map of responses being added(Component/Page, Map(key, List/TimedValue(AuResponse))). */ private Map _responses; /** A stack of components that are including new pages (and then * become the owner of the new page, if any). */ private final List _owners; /** Time stamp for smart update and responses (see {@link TimedValue}). */ private int _timed; /** if not null, it means the current executing is aborting * and the content is reason to aborting. Its interpretation depends * on {@link org.zkoss.zk.ui.sys.UiEngine}. */ private AbortingReason _aborting; /** Whether the first execution (_1stec) is for async-update. */ private final boolean _1stau; /** * Creates a root execution (without parent). * In other words, it must be the first execution in the current request. * * @param asyncUpdate whether this exec is for async-update */ public UiVisualizer(Execution exec, boolean asyncUpdate) { _exec = exec; _1stec = this; _1stau = asyncUpdate; _owners = new LinkedList(); } /** * Creates the following execution. * The first execution must use {@link #UiVisualizer(Execution, boolean)} */ public UiVisualizer(UiVisualizer parent, Execution exec) { _exec = exec; _1stec = parent._1stec; _1stau = parent._1stau; _owners = null; } //-- Visualizer --// public final Execution getExecution() { return _exec; } public final boolean isEverAsyncUpdate() { return _1stau; } public final boolean addToFirstAsyncUpdate(List responses) { if (!_1stau) return false; if (D.ON && log.finerable()) log.finer("Add to 1st au: "+responses); for (Iterator it = responses.iterator(); it.hasNext();) _1stec.addResponse(null, (AuResponse)it.next()); return true; } //-- update/redraw --// /** Invalidates the whole page. */ public void addInvalidate(Page page) { if (!_exec.isAsyncUpdate(page)) return; //nothing to do if (_pgInvalid == null) _pgInvalid = new LinkedHashSet(7); _pgInvalid.add(page); } /** Adds an invalidated component. Once invalidated, all invocations * to {@link #addSmartUpdate} are ignored in this execution. */ public void addInvalidate(Component comp) { if (!_exec.isAsyncUpdate(comp.getPage())) return; //nothing to do if (_invalidated.add(comp)) _smartUpdated.remove(comp); } /** Smart updates a component's attribute. * Meaningful only if {@link #addInvalidate(Component)} is not called in this * execution */ public void addSmartUpdate(Component comp, String attr, String value) { if (!_exec.isAsyncUpdate(comp.getPage()) || _invalidated.contains(comp)) return; //nothing to do Map respmap = (Map)_smartUpdated.get(comp); if (respmap == null) _smartUpdated.put(comp, respmap = new HashMap()); respmap.put(attr, new TimedValue(_timed++, comp, attr, value)); } /** Called to update (redraw) a component, when a component is moved. * If a component's page or parent is changed, this method need to be * called only once for the top one. * * @param oldparent the parent before moved * @param oldpg the page before moved * @param newpg the page after moved */ public void addMoved(Component comp, Component oldparent, Page oldpg, Page newpg) { if ((newpg == null && !_exec.isAsyncUpdate(oldpg)) //detach from loading pg || (oldpg == null && !_exec.isAsyncUpdate(newpg))) //attach to loading pg return; //to avoid redundant AuRemove if (oldpg == null && !_moved.contains(comp)) { //new attached _attached.add(comp); //note: we cannot examine _exec.isAsyncUpdate here because //comp.getPage might be ready when this method is called } else { if (_moved.add(comp)) { //first time added //Due to the performance of Cropper.getAvailableAtClient //usually not good, so we count on isCropper only //In other words, this algorithm might redraw something that don't //need to, but has better performance if no redudant redrawing if (oldparent != null) { final Object xc = ((ComponentCtrl)oldparent).getExtraCtrl(); if ((xc instanceof Cropper) && ((Cropper)xc).isCropper()) _invalidated.add(oldparent); } } _attached.remove(comp); } } /** Called before changing the component's UUID. * * @param addOnlyMoved if true, it is added only if it was moved * before (see {@link #addMoved}). */ public void addUuidChanged(Component comp, boolean addOnlyMoved) { if ((!addOnlyMoved || _moved.contains(comp)) && (_idChgd == null || !_idChgd.containsKey(comp))) { if (_idChgd == null) _idChgd = new LinkedHashMap(23); _idChgd.put(comp, comp.getUuid()); } } /** Adds a response directly (which will be returned when * {@link #getResponses} is called). * * <p>If the response is component-dependent, {@link AuResponse#getDepends} * must return a component. And, if the component is removed, the response * is removed, too. * * @param key could be anything. The second invocation of this method * in the same execution with the same key will override the previous one. */ public void addResponse(String key, AuResponse response) { if (response == null) throw new IllegalArgumentException(); if (_responses == null) _responses = new HashMap(); final Object depends = response.getDepends(); //Page or Component Map respmap = (Map)_responses.get(depends); if (respmap == null) _responses.put(depends, respmap = new HashMap()); final TimedValue tval = new TimedValue(_timed++, response); if (key != null) { respmap.put(key, tval); } else { List resps = (List)respmap.get(null); if (resps == null) respmap.put(null, resps = new LinkedList()); resps.add(tval); //don't overwrite } } /** Process {@link Cropper}. * * <p>Note: it is too late to handle the moved/removed components here, * since we don't know their old parent now! * Rather, we handle it in {@link #addMoved}. * * @return whether any new invalidated is added */ private boolean doCrop() { final Map cropping = new HashMap(); boolean invAdded = crop(_attached, cropping, true, false); invAdded = crop(_smartUpdated.keySet(), cropping, false, false) || invAdded; if (_responses != null) invAdded = crop(_responses.keySet(), cropping, false, true) || invAdded; invAdded = crop(_invalidated, cropping, false, false) || invAdded; //crop invalidate as the last step since new invalidated might be added return invAdded; } /** Crop attached and moved. * * @return whether any new invalidated is added */ private boolean crop(Set coll, Map cropping, boolean bAttached, boolean bResponse) { Set newInvalid = null; for (Iterator it = coll.iterator(); it.hasNext();) { final Object o = it.next(); if (!(o instanceof Component)) continue; final Component comp = (Component)o; if (!_exec.isAsyncUpdate(comp.getPage())) { if (!bResponse) it.remove(); //just in case continue; } for (Component p, c = comp; (p = c.getParent()) != null; c = p) { final Set avail = getAvailableAtClient(p, cropping); if (avail != null) { if (bAttached) { if (avail.contains(c)) { if (c != comp) continue; //not direct child, do as if not cropper //don't add to _invalidate directly since coll might //be _invalidate if (newInvalid == null) newInvalid = new HashSet(); newInvalid.add(p); } it.remove(); break; } else if (!avail.contains(c)) { it.remove(); break; } } } } return newInvalid != null && _invalidated.addAll(newInvalid); } private static Set getAvailableAtClient(Component comp, Map cropping) { final Object xc = ((ComponentCtrl)comp).getExtraCtrl(); if (xc instanceof Cropper) { //we don't need to check isCropper first since its component's job //to ensure the consistency Set set = (Set)cropping.get(comp); if (set != null) return set != Collections.EMPTY_SET ? set: null;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -