001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.tax;
042:
043: import org.netbeans.tax.spec.Document;
044: import org.netbeans.tax.spec.DocumentType;
045: import org.netbeans.tax.spec.DTD;
046:
047: import java.util.*;
048:
049: /**
050: *
051: * @author Libor Kramolis
052: * @version 0.1
053: */
054: public class TreeDocumentType extends AbstractTreeDTD implements
055: TreeDTDRoot, Document.Child {
056: /** */
057: public static final String PROP_ELEMENT_NAME = "elementName"; // NOI18N
058: /** */
059: public static final String PROP_PUBLIC_ID = "publicId"; // NOI18N
060: /** */
061: public static final String PROP_SYSTEM_ID = "systemId"; // NOI18N
062:
063: /** */
064: private String elementName;
065:
066: /** -- can be null. */
067: private String publicId;
068:
069: /** -- can be null. */
070: private String systemId;
071:
072: // strong reference to keep a key in bellow map
073: private DTDIdentity dtdIdentity;
074:
075: // holds DTD-ID -> TreeDocumentFragment mapping
076: private static final WeakHashMap externalEntities = new WeakHashMap();
077:
078: private String internalDTDText; //!!! it is accesed by introspection, it a hack
079:
080: //
081: // init
082: //
083:
084: /**
085: * Creates new TreeDocumentType.
086: * @throws InvalidArgumentException
087: */
088: public TreeDocumentType(String elementName, String publicId,
089: String systemId) throws InvalidArgumentException {
090: super ();
091:
092: checkElementName(elementName);
093: checkPublicId(publicId);
094: checkSystemId(systemId);
095:
096: this .elementName = elementName;
097: this .publicId = publicId;
098: this .systemId = systemId;
099: this .dtdIdentity = new DTDIdentity();
100:
101: }
102:
103: /** Creates new TreeDocumentType.
104: * @throws InvalidArgumentException
105: */
106: public TreeDocumentType(String elementName)
107: throws InvalidArgumentException {
108: this (elementName, null, null);
109: }
110:
111: /** Creates new TreeDocumentType -- copy constructor. */
112: protected TreeDocumentType(TreeDocumentType documentType,
113: boolean deep) {
114: super (documentType, deep);
115:
116: this .elementName = documentType.elementName;
117: this .publicId = documentType.publicId;
118: this .systemId = documentType.systemId;
119: this .internalDTDText = documentType.internalDTDText;
120: this .dtdIdentity = documentType.dtdIdentity;
121: }
122:
123: //
124: // from TreeObject
125: //
126:
127: /**
128: */
129: public Object clone(boolean deep) {
130: return new TreeDocumentType(this , deep);
131: }
132:
133: /**
134: */
135: public boolean equals(Object object, boolean deep) {
136: if (!!!super .equals(object, deep))
137: return false;
138:
139: TreeDocumentType peer = (TreeDocumentType) object;
140: if (!!!Util
141: .equals(this .getElementName(), peer.getElementName()))
142: return false;
143: if (!!!Util.equals(this .getPublicId(), peer.getPublicId()))
144: return false;
145: if (!!!Util.equals(this .getSystemId(), peer.getSystemId()))
146: return false;
147: if (!!!Util.equals(this .dtdIdentity, peer.dtdIdentity))
148: return false;
149:
150: return true;
151: }
152:
153: /*
154: * Merges documet root name, publicId and system ID properties.
155: * External DTD list merging is delegated.
156: */
157: public void merge(TreeObject treeObject)
158: throws CannotMergeException {
159: super .merge(treeObject);
160:
161: TreeDocumentType peer = (TreeDocumentType) treeObject;
162:
163: setElementNameImpl(peer.getElementName());
164: setPublicIdImpl(peer.getPublicId());
165: setSystemIdImpl(peer.getSystemId());
166: internalDTDText = peer.internalDTDText;
167: dtdIdentity = peer.dtdIdentity;
168: }
169:
170: //
171: // read only
172: //
173:
174: /**
175: * It's not propagated to exteranl entity. It's always read only.
176: */
177: protected void setReadOnly(boolean newReadOnly) {
178: super .setReadOnly(newReadOnly);
179: }
180:
181: //
182: // parent
183: //
184:
185: /**
186: */
187: public boolean hasChildNodes(Class childClass, boolean recursive) {
188: TreeObjectList external = getExternalDTD();
189: Iterator externalIterator = external != null ? external
190: .iterator() : Collections.EMPTY_SET.iterator();
191: Iterator[] its = new Iterator[] { getChildNodes().iterator(),
192: externalIterator };
193:
194: for (int i = 0; i < its.length; i++) {
195: Iterator it = its[i];
196: while (it.hasNext()) {
197: TreeChild child = (TreeChild) it.next();
198:
199: // add matching leaf node
200:
201: if (childClass == null
202: || childClass
203: .isAssignableFrom(child.getClass())) {
204: return true;
205: }
206:
207: // do recursive descent into kids
208:
209: if (recursive && (child instanceof TreeParentNode)) {
210: if (((TreeParentNode) child).hasChildNodes(
211: childClass, true) == true) {
212: return true;
213: }
214: }
215: }
216: }
217: return false;
218: }
219:
220: /**
221: * @return copy collection containing references from internal and
222: * optionally external part of DTD
223: */
224: public Collection getChildNodes(Class childClass, boolean recursive) {
225: Collection allChildNodes = new LinkedList();
226: TreeObjectList external = getExternalDTD();
227: Iterator externalIterator = external != null ? external
228: .iterator() : Collections.EMPTY_SET.iterator();
229:
230: Iterator[] its = new Iterator[] { getChildNodes().iterator(),
231: externalIterator };
232:
233: for (int i = 0; i < its.length; i++) {
234: Iterator it = its[i];
235: while (it.hasNext()) {
236: TreeChild child = (TreeChild) it.next();
237: if (childClass == null
238: || childClass
239: .isAssignableFrom(child.getClass())) {
240: allChildNodes.add(child);
241: }
242:
243: if (recursive && (child instanceof TreeParentNode)) {
244: allChildNodes.addAll(((TreeParentNode) child)
245: .getChildNodes(childClass, true));
246: }
247: }
248: }
249:
250: return allChildNodes;
251: }
252:
253: //
254: // itself
255: //
256:
257: /**
258: * Return read only child list representing external DTD content
259: * or <code>null</code> if unknown.
260: */
261: public final TreeObjectList getExternalDTD() {
262: TreeDTDFragment fragment = (TreeDTDFragment) externalEntities
263: .get(dtdIdentity);
264: if (fragment == null) {
265: return null;
266: } else {
267: return fragment.getChildNodes();
268: }
269: }
270:
271: /**
272: */
273: public final String getElementName() {
274: return elementName;
275: }
276:
277: /**
278: */
279: private final void setElementNameImpl(String newElementName) {
280: String oldElementName = this .elementName;
281:
282: this .elementName = newElementName;
283:
284: firePropertyChange(PROP_ELEMENT_NAME, oldElementName,
285: newElementName);
286: }
287:
288: /**
289: * @throws ReadOnlyException
290: * @throws InvalidArgumentException
291: */
292: public final void setElementName(String newElementName)
293: throws ReadOnlyException, InvalidArgumentException {
294: //
295: // check new value
296: //
297: if (Util.equals(this .elementName, newElementName))
298: return;
299: checkReadOnly();
300: checkElementName(newElementName);
301:
302: //
303: // set new value
304: //
305: setElementNameImpl(newElementName);
306: }
307:
308: /**
309: */
310: protected final void checkElementName(String elementName)
311: throws InvalidArgumentException {
312: TreeUtilities.checkDocumentTypeElementName(elementName);
313: }
314:
315: /**
316: */
317: public final String getPublicId() {
318: return publicId;
319: }
320:
321: /**
322: */
323: private final void setPublicIdImpl(String newPublicId) {
324: String oldPublicId = this .publicId;
325:
326: this .publicId = newPublicId;
327:
328: firePropertyChange(PROP_PUBLIC_ID, oldPublicId, newPublicId);
329: }
330:
331: /**
332: * @throws ReadOnlyException
333: * @throws InvalidArgumentException
334: */
335: public final void setPublicId(String newPublicId)
336: throws ReadOnlyException, InvalidArgumentException {
337: //
338: // check new value
339: //
340: if (Util.equals(this .publicId, newPublicId))
341: return;
342: checkReadOnly();
343: checkPublicId(newPublicId);
344:
345: //
346: // set new value
347: //
348: setPublicIdImpl(newPublicId);
349: }
350:
351: /**
352: */
353: protected final void checkPublicId(String publicId)
354: throws InvalidArgumentException {
355: TreeUtilities.checkDocumentTypePublicId(publicId);
356: }
357:
358: /**
359: */
360: public final String getSystemId() {
361: return systemId;
362: }
363:
364: /**
365: */
366: private final void setSystemIdImpl(String newSystemId) {
367: String oldSystemId = this .systemId;
368:
369: this .systemId = newSystemId;
370:
371: firePropertyChange(PROP_SYSTEM_ID, oldSystemId, newSystemId);
372: }
373:
374: /**
375: * @throws ReadOnlyException
376: * @throws InvalidArgumentException
377: */
378: public final void setSystemId(String newSystemId)
379: throws ReadOnlyException, InvalidArgumentException {
380: //
381: // check new value
382: //
383: if (Util.equals(this .systemId, newSystemId))
384: return;
385: checkReadOnly();
386: checkSystemId(newSystemId);
387:
388: //
389: // set new value
390: //
391: setSystemIdImpl(newSystemId);
392: }
393:
394: /**
395: */
396: protected final void checkSystemId(String systemId)
397: throws InvalidArgumentException {
398: TreeUtilities.checkDocumentTypeSystemId(systemId);
399: }
400:
401: //
402: // TreeObjectList.ContentManager
403: //
404:
405: /**
406: */
407: protected TreeObjectList.ContentManager createChildListContentManager() {
408: return new ChildListContentManager();
409: }
410:
411: /**
412: * Internal DTD content manager.
413: * All kids use as parent node wrapping TreeDocumentType.
414: * All kids must be DocumentType.Child instances.
415: */
416: protected class ChildListContentManager extends
417: AbstractTreeDTD.ChildListContentManager {
418:
419: /**
420: */
421: public TreeNode getOwnerNode() {
422: return TreeDocumentType.this ;
423: }
424:
425: /**
426: */
427: public void checkAssignableObject(Object obj) {
428: super .checkAssignableObject(obj);
429: checkAssignableClass(DocumentType.Child.class, obj);
430: }
431:
432: } // end: class ChildListContentManager
433:
434: /**
435: * Get DTDIdentity proxy for this class. It's a live object.
436: */
437: public final DTDIdentity getDTDIdentity() {
438: return dtdIdentity;
439: }
440:
441: /**
442: * Set new external DTD model. Note that it can be shared by
443: * several TreeDocumentType instances.
444: */
445: public final void setExternalDTD(TreeDocumentFragment externalDTD) {
446: externalEntities.put(getDTDIdentity(), externalDTD);
447: }
448:
449: /**
450: * Defines doctype identity based on its public ID and system ID pairs.
451: * Can be used as key if such equalince/identity is required.
452: * @see #getDTDIdentity
453: */
454: public final class DTDIdentity {
455:
456: private DTDIdentity() {
457: }
458:
459: private String getPublicId() {
460: return publicId;
461: }
462:
463: private String getSystemId() {
464: return systemId;
465: }
466:
467: public boolean equals(Object o) {
468: if (o == this )
469: return true;
470: if (o instanceof DTDIdentity) {
471: DTDIdentity peer = (DTDIdentity) o;
472: if (Util.equals(peer.getPublicId(), publicId) == false)
473: return false;
474: if (Util.equals(peer.getSystemId(), systemId) == false)
475: return false;
476: return true;
477: }
478: return false;
479: }
480:
481: public int hashCode() {
482: int h1 = publicId != null ? publicId.hashCode() : 13;
483: int h2 = systemId != null ? systemId.hashCode() : 37;
484: return h1 ^ h2;
485: }
486: }
487: }
|