001: /* Components.java
002:
003: {{IS_NOTE
004: Purpose:
005:
006: Description:
007:
008: History:
009: Mon Jun 13 20:55:18 2005, Created by tomyeh
010: }}IS_NOTE
011:
012: Copyright (C) 2005 Potix Corporation. All Rights Reserved.
013:
014: {{IS_RIGHT
015: This program is distributed under GPL Version 2.0 in the hope that
016: it will be useful, but WITHOUT ANY WARRANTY.
017: }}IS_RIGHT
018: */
019: package org.zkoss.zk.ui;
020:
021: import java.util.Iterator;
022: import java.util.ListIterator;
023: import java.util.Collection;
024: import java.util.AbstractCollection;
025: import java.util.ArrayList;
026: import java.util.List;
027: import java.util.Collections;
028: import java.util.Arrays;
029: import java.util.Comparator;
030:
031: /**
032: * Utilities to access {@link Component}.
033: *
034: * @author tomyeh
035: */
036: public class Components {
037: protected Components() {
038: }
039:
040: /** Sorts the components in the list.
041: *
042: * <p>Note: you cannot use Collections.sort to sort
043: * {@link Component#getChildren} because Collections.sort might cause
044: * some replicated item in the list.
045: */
046: public static void sort(List list, Comparator cpr) {
047: final Object ary[] = list.toArray();
048: Arrays.sort(ary, cpr);
049:
050: ListIterator it = list.listIterator();
051: int j = 0;
052: for (; it.hasNext(); ++j) {
053: if (it.next() != ary[j]) {
054: it.remove();
055:
056: if (it.hasNext()) {
057: if (it.next() == ary[j])
058: continue;
059: it.previous();
060: }
061: break;
062: }
063: }
064: while (it.hasNext()) {
065: it.next();
066: it.remove();
067: }
068: for (; j < ary.length; ++j)
069: list.add(ary[j]);
070: }
071:
072: /** Tests whether node1 is an ancessor of node 2.
073: * If node1 and node2 is the same, true is returned.
074: */
075: public static boolean isAncestor(Component node1, Component node2) {
076: for (; node2 != null; node2 = node2.getParent()) {
077: if (node1 == node2)
078: return true;
079: }
080: return false;
081: }
082:
083: /** Removes all children of the specified component.
084: */
085: public static void removeAllChildren(Component comp) {
086: final List children = comp.getChildren();
087: if (children.isEmpty())
088: return;
089:
090: for (Iterator it = new ArrayList(children).iterator(); it
091: .hasNext();)
092: ((Component) it.next()).setParent(null); //detach
093: }
094:
095: /** Returns whether this component is real visible (all its parents
096: * are visible).
097: * @see Component#isVisible
098: */
099: public static boolean isRealVisible(Component comp) {
100: for (; comp != null; comp = comp.getParent())
101: if (!comp.isVisible())
102: return false;
103: return true;
104: }
105:
106: /** Returns a collection of visible children.
107: * <p>The performance of the returned collection's size() is NO GOOD.
108: */
109: public static Collection getVisibleChildren(Component comp) {
110: final Collection children = comp.getChildren();
111: return new AbstractCollection() {
112: public int size() {
113: int size = 0;
114: for (Iterator it = children.iterator(); it.hasNext();) {
115: if (((Component) it.next()).isVisible())
116: ++size;
117: }
118: return size;
119: }
120:
121: public Iterator iterator() {
122: return new Iterator() {
123: final Iterator _it = children.iterator();
124: Component _next;
125:
126: public boolean hasNext() {
127: if (_next != null)
128: return true;
129: _next = getNextVisible(false);
130: return _next != null;
131: }
132:
133: public Object next() {
134: if (_next != null) {
135: final Component c = _next;
136: _next = null;
137: return c;
138: }
139: return getNextVisible(true);
140: }
141:
142: public void remove() {
143: throw new UnsupportedOperationException();
144: }
145:
146: private Component getNextVisible(boolean blind) {
147: while (blind || _it.hasNext()) {
148: final Component c = (Component) _it.next();
149: if (c.isVisible())
150: return c;
151: }
152: return null;
153: }
154: };
155: }
156: };
157: }
158:
159: /** Converts a string to an integer that can be used to access
160: * {@link Component#getAttribute(String, int)}
161: */
162: public static final int getScope(String scope) {
163: if ("component".equals(scope))
164: return Component.COMPONENT_SCOPE;
165: if ("space".equals(scope))
166: return Component.SPACE_SCOPE;
167: if ("page".equals(scope))
168: return Component.PAGE_SCOPE;
169: if ("desktop".equals(scope))
170: return Component.DESKTOP_SCOPE;
171: if ("session".equals(scope))
172: return Component.SESSION_SCOPE;
173: if ("application".equals(scope))
174: return Component.APPLICATION_SCOPE;
175: if ("request".equals(scope))
176: return Component.REQUEST_SCOPE;
177: throw new IllegalArgumentException("Unknown scope: " + scope);
178: }
179:
180: /** Converts an integer to the string representing the scope.
181: * @param scope one of {@link Component#COMPONENT_SCOPE},
182: * {@link Component#SPACE_SCOPE}, {@link Component#PAGE_SCOPE},
183: * {@link Component#DESKTOP_SCOPE}, {@link Component#SESSION_SCOPE},
184: * {@link Component#REQUEST_SCOPE}, and {@link Component#APPLICATION_SCOPE}.
185: */
186: public static final String scopeToString(int scope) {
187: switch (scope) {
188: case Component.COMPONENT_SCOPE:
189: return "component";
190: case Component.SPACE_SCOPE:
191: return "space";
192: case Component.PAGE_SCOPE:
193: return "page";
194: case Component.DESKTOP_SCOPE:
195: return "desktop";
196: case Component.SESSION_SCOPE:
197: return "session";
198: case Component.APPLICATION_SCOPE:
199: return "application";
200: case Component.REQUEST_SCOPE:
201: return "request";
202: }
203: throw new IllegalArgumentException("Unknown scope: " + scope);
204: }
205:
206: /** Returns whether an ID is generated automatically.
207: */
208: public static final boolean isAutoId(String id) {
209: return org.zkoss.zk.ui.sys.ComponentsCtrl.isAutoId(id);
210: }
211:
212: /** Converts a component to a path (relavant to another component).
213: * It is usefully to implement a serializable component that contains
214: * a reference to another component. In this case, we can not
215: * serializes the reference directly (otherwise, another component will
216: * be created, when deserialized).
217: *
218: * <p>Rather, it is better to store the path related, and then restore
219: * it back to a component by calling {@link #pathToComponent}.
220: *
221: * @param comp the component to be converted to path. It cannot be null.
222: * @param ref the component used to generated the path from.
223: * It cannot be null.
224: * @return the path. Notice that you have to use {@link #pathToComponent}
225: * to convert it back.
226: * @exception UnsupportedOperationException if we cannot find a path
227: * to the component to write.
228: * @since 3.0.0
229: */
230: public static final String componentToPath(Component comp,
231: Component ref) {
232: //Implementation Note:
233: //The path being written is a bit different to Path, if ref
234: //is not an space owner
235: //For example, if comp is the space owner, "" is written.
236: //If comp is the same as ref, "." is written.
237: if (comp == null) {
238: return null;
239: } else if (comp == ref) {
240: return ".";
241: } else {
242: final String id = comp.getId();
243: if (!(comp instanceof IdSpace) && isAutoId(id))
244: throw new UnsupportedOperationException(
245: "comp must be assigned with ID or a space owner: "
246: + comp);
247:
248: final StringBuffer sb = new StringBuffer(128);
249: for (IdSpace space = ref.getSpaceOwner();;) {
250: if (comp == space) {
251: return sb.toString(); //could be ""
252: //we don't generate id to make it work even if
253: //its ID is changed
254: } else if (space.getFellowIfAny(id) == comp) {
255: if (sb.length() > 0)
256: sb.append('/');
257: return sb.append(id).toString();
258: }
259:
260: if (sb.length() > 0)
261: sb.append('/');
262: sb.append("..");
263:
264: final Component parent = space instanceof Component ? ((Component) space)
265: .getParent()
266: : null;
267: if (parent == null)
268: throw new UnsupportedOperationException(
269: "Unable to locate " + comp + " from " + ref);
270: space = parent.getSpaceOwner();
271: }
272: }
273: }
274:
275: /** Converts a path, generated by {@link #componentToPath}, to
276: * a component.
277: *
278: * @param ref the component used to generated the path from.
279: * It cannot be null. It is the same as the one when calling
280: * {@link #componentToPath}.
281: * @since 3.0.0
282: */
283: public static final Component pathToComponent(String path,
284: Component ref) {
285: if (path == null) {
286: return null;
287: } else if (".".equals(path)) {
288: return ref;
289: } else if ("".equals(path)) {
290: final IdSpace owner = ref.getSpaceOwner();
291: if (!(owner instanceof Component))
292: throw new IllegalStateException(
293: "The component is moved after serialized: "
294: + ref);
295: return (Component) owner;
296: }
297: return Path.getComponent(ref.getSpaceOwner(), path);
298: }
299: }
|