001: /* AbstractItem.java
002:
003: {{IS_NOTE
004:
005: Purpose:
006: Description:
007: History:
008: 2001/10/21 16:26:46, Create, Tom M. Yeh.
009: }}IS_NOTE
010:
011: Copyright (C) 2001 Potix Corporation. All Rights Reserved.
012:
013: {{IS_RIGHT
014: This program is distributed under GPL Version 2.0 in the hope that
015: it will be useful, but WITHOUT ANY WARRANTY.
016: }}IS_RIGHT
017: */
018: package org.zkoss.idom.impl;
019:
020: import java.util.List;
021: import java.util.Collections;
022: import java.util.Map;
023: import java.util.HashMap;
024: import java.io.Serializable;
025: import java.util.regex.Pattern;
026:
027: import org.w3c.dom.Node;
028: import org.w3c.dom.NodeList;
029: import org.w3c.dom.NamedNodeMap;
030: import org.w3c.dom.UserDataHandler;
031:
032: import org.zkoss.xml.Locator;
033: import org.zkoss.xml.Nodes;
034: import org.zkoss.idom.*;
035:
036: /**
037: * A semi-implemented item for leaf vertices.
038: * A leaf item is a item without any children.
039: * For cores having child cores, use Group.
040: *
041: * <p>Important methods that the deriving might want to override.
042: *
043: * <dl>
044: * <dt>Item.getName</dt>
045: * <dd>It must be overrided to provide a local name.</dd>
046: * <dt>Item.setName</dt>
047: * <dd>Overrid it if it allows to change the name.
048: * Default: throws an exception.</dd>
049: * <dt>Item.getText</dt>
050: * <dd>Override it if it has a text representation.
051: * Default: returns null.</dd>
052: * <dt>Item.setText</dt>
053: * <dd>Overrid it if it allows to change the text.
054: * Default: throws an exception.</dd>
055: * <dt>Node.getNodeType</dt>
056: * <dd>It must be overrided to provide the type.</dd>
057: * <dt>Item.clone</dt>
058: * <dd>Override it if any other members to handle specially.
059: * Note: by definition, we do deep clone only.</dd>
060: * <dt>Object.toString</dt>
061: * <dd>Override if you want a representation other than
062: * XMLOutputter does.</dd>
063: * </dl>
064: *
065: * @author tomyeh
066: * @see org.zkoss.idom.Item
067: */
068: public abstract class AbstractItem implements Item, Node, Serializable,
069: Cloneable {
070: private static final long serialVersionUID = 20060622L;
071:
072: /** The parent. */
073: private Group _parent;
074: /** The locator. */
075: private Locator _loc;
076: /** The read-only flag. */
077: private boolean _readonly;
078: /** The map of user data. */
079: private Map _usrdta;
080: /** The modification flag. */
081: protected transient boolean _modified;
082:
083: /** Constructor.
084: */
085: protected AbstractItem() {
086: }
087:
088: //-- utilities --//
089: /** Checks whether this item is writable (ie, isReady).
090: * @exception DOMException with NO_MODIFICATION_ALLOWED_ERR if not writable
091: */
092: protected final void checkWritable() {
093: if (isReadonly())
094: throw new DOMException(
095: DOMException.NO_MODIFICATION_ALLOWED_ERR, _loc);
096: }
097:
098: //-- utilities --//
099: /** Tests whether a namespaceable item matches the criteria.
100: * If mode don't contain FIND_BY_REGEX, ptn is ignored.
101: */
102: protected static boolean match(Namespaceable vtx, String namespace,
103: String name, Pattern ptn, int mode) {
104: if (name != null) {
105: String val = (mode & FIND_BY_TAGNAME) != 0 ? vtx
106: .getTagName() : vtx.getLocalName();
107: if ((mode & FIND_BY_REGEX) != 0) {
108: if (!ptn.matcher(val).matches())
109: return false;
110: } else if ((mode & FIND_IGNORE_CASE) != 0) {
111: if (!name.equalsIgnoreCase(val))
112: return false;
113: } else {
114: if (!name.equals(val))
115: return false;
116: }
117: }
118:
119: if (namespace != null) {
120: String val = (mode & FIND_BY_PREFIX) != 0 ? vtx
121: .getNamespace().getPrefix() : vtx.getNamespace()
122: .getURI();
123: if (!namespace.equals(val))
124: return false;
125: }
126: return true;
127: }
128:
129: //-- Item --//
130: public boolean isReadonly() {
131: return _readonly || (_parent != null && _parent.isReadonly());
132: }
133:
134: /**
135: * Sets the read-only flag of this item. It causes this item
136: * and all its descendants read-only, see {@link #isReadonly}.
137: *
138: * <p>Deriving class might restrict more conditions here.
139: */
140: public void setReadonly(boolean readonly) {
141: _readonly = readonly;
142: }
143:
144: public boolean isModified() {
145: return _modified;
146: }
147:
148: public void clearModified(boolean includingDescendant) {
149: _modified = false;
150: }
151:
152: public void setModified() {
153: _modified = true;
154: if (_parent != null)
155: _parent.setModified();
156: }
157:
158: public void setName(String name) {
159: throw new DOMException(DOMException.INVALID_ACCESS_ERR, _loc);
160: }
161:
162: public String getText() {
163: return null;
164: }
165:
166: public void setText(String text) {
167: throw new DOMException(DOMException.INVALID_ACCESS_ERR, _loc);
168: }
169:
170: public Document getDocument() {
171: for (Item v = _parent; v != null; v = v.getParent())
172: if (v instanceof Document)
173: return (Document) v;
174: return null;
175: }
176:
177: public Item detach() {
178: checkWritable();
179: if (_parent != null) {
180: _parent.getChildren().remove(this );
181: assert (_parent == null);
182: assert (isModified());
183: }
184: return this ;
185: }
186:
187: public Item clone(boolean preserveModified) {
188: try {
189: AbstractItem v = (AbstractItem) super .clone();
190: v._readonly = false;
191: v._parent = null;
192: if (!preserveModified)
193: v._modified = false;
194: return v;
195: } catch (CloneNotSupportedException ex) {
196: throw new InternalError();
197: }
198: }
199:
200: public final Group getParent() {
201: return _parent;
202: }
203:
204: public void setParent(Group parent) {
205: checkWritable();
206: if (_parent != parent) {
207: //Note: new parent and old parent's setModified must be called
208: if (_parent != null)
209: _parent.setModified();
210: _parent = parent;
211: setModified(); //then, parent.setModified is called
212: }
213: }
214:
215: public final Locator getLocator() {
216: return _loc;
217: }
218:
219: public final void setLocator(Locator loc) {
220: checkWritable();
221: _loc = loc;
222: }
223:
224: //-- Node --//
225: public String getNodeName() {
226: return getName();
227: }
228:
229: public String getNodeValue() {
230: return getText();
231: }
232:
233: public void setNodeValue(String nodeValue) {
234: setText(nodeValue);
235: }
236:
237: public org.w3c.dom.Document getOwnerDocument() {
238: return getDocument();
239: }
240:
241: public final Node cloneNode(boolean deep) {
242: if (!deep)
243: throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
244: "always deep", _loc); //NOT YET
245: return (Node) clone(false);
246: }
247:
248: public final Node getParentNode() {
249: return (Node) getParent();
250: }
251:
252: public final Node getPreviousSibling() {
253: if (_parent == null)
254: return null;
255:
256: List list = _parent.getChildren();
257: int j = list.indexOf(this );
258: if (j < 0)
259: throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR,
260: "internal error", _loc);
261: return j == 0 ? null : (Node) list.get(j - 1);
262: }
263:
264: public final Node getNextSibling() {
265: if (_parent == null)
266: return null;
267:
268: List list = _parent.getChildren();
269: int j = list.indexOf(this );
270: if (j < 0)
271: throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR,
272: "internal error", _loc);
273: return j == list.size() - 1 ? null : (Node) list.get(j + 1);
274: }
275:
276: public NodeList getChildNodes() {
277: return Nodes.EMPTY_NODELIST;
278: }
279:
280: public Node getFirstChild() {
281: return null;
282: }
283:
284: public Node getLastChild() {
285: return null;
286: }
287:
288: public boolean hasChildNodes() {
289: return false;
290: }
291:
292: public Node insertBefore(Node newChild, Node refChild) {
293: throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, _loc);
294: }
295:
296: public Node replaceChild(Node newChild, Node oldChild) {
297: throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, _loc);
298: }
299:
300: public Node removeChild(Node oldChild) {
301: throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, _loc);
302: }
303:
304: public Node appendChild(Node newChild) {
305: throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, _loc);
306: }
307:
308: public final void normalize() {
309: throw new DOMException(DOMException.NOT_SUPPORTED_ERR, _loc); //NOT YET
310: }
311:
312: public final boolean isSupported(String feature, String version) {
313: return DOMImplementation.THE.hasFeature(feature, version);
314: }
315:
316: public String getNamespaceURI() {
317: return null;
318: }
319:
320: public String getPrefix() {
321: return null;
322: }
323:
324: public String getLocalName() {
325: return getName();
326: }
327:
328: public void setPrefix(String prefix) {
329: throw new DOMException(DOMException.INVALID_ACCESS_ERR, _loc);
330: }
331:
332: public NamedNodeMap getAttributes() {
333: return EmptyNamedNodeMap.THE;
334: }
335:
336: public boolean hasAttributes() {
337: return false;
338: }
339:
340: public String getBaseURI() {
341: return null; //Level 3 not yet
342: }
343:
344: public short compareDocumentPosition(Node other)
345: throws DOMException {
346: throw new UnsupportedOperationException("DOM Level 3");
347: }
348:
349: public String getTextContent() throws DOMException {
350: throw new UnsupportedOperationException("DOM Level 3");
351: }
352:
353: public void setTextContent(String textContent) throws DOMException {
354: throw new UnsupportedOperationException("DOM Level 3");
355: }
356:
357: public boolean isSameNode(Node other) {
358: throw new UnsupportedOperationException("DOM Level 3");
359: }
360:
361: public String lookupPrefix(String namespaceURI) {
362: throw new UnsupportedOperationException("DOM Level 3");
363: }
364:
365: public boolean isDefaultNamespace(String namespaceURI) {
366: throw new UnsupportedOperationException("DOM Level 3");
367: }
368:
369: public String lookupNamespaceURI(String prefix) {
370: throw new UnsupportedOperationException("DOM Level 3");
371: }
372:
373: public boolean isEqualNode(Node arg) {
374: throw new UnsupportedOperationException("DOM Level 3");
375: }
376:
377: public Object getFeature(String feature, String version) {
378: return null;
379: }
380:
381: public Object setUserData(String key, Object data,
382: UserDataHandler handler) {
383: if (handler != null)
384: throw new UnsupportedOperationException("DOM Level 3");
385: if (_usrdta == null)
386: _usrdta = new HashMap();
387: return _usrdta.put(key, data);
388: }
389:
390: public Object getUserData(String key) {
391: return _usrdta != null ? _usrdta.get(key) : null;
392: }
393:
394: //-- Object --//
395: /** Overriding this method is prohibited.
396: */
397: public final boolean equals(Object o) {
398: return this == o;
399: }
400:
401: /** Overriding this method is prohibited.
402: */
403: public final int hashCode() {
404: return super .hashCode();
405: }
406:
407: //-- Cloneable --//
408: /**
409: * Clones this object (a deep cloning not including contents contained
410: * in Textual nodes).
411: * Note: after cloning, the read-only flag always becomes false,
412: * and the parent becomes null (i.e., detached).
413: */
414: public Object clone() {
415: return clone(false);
416: }
417: }
|