KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > xml > xam > dom > AbstractDocumentComponent


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.xml.xam.dom;
21
22 import java.io.IOException JavaDoc;
23 import java.net.URI JavaDoc;
24 import java.net.URISyntaxException JavaDoc;
25 import java.util.ArrayList JavaDoc;
26 import java.util.HashMap JavaDoc;
27 import java.util.List JavaDoc;
28 import java.util.Map JavaDoc;
29 import java.util.logging.Level JavaDoc;
30 import java.util.logging.Logger JavaDoc;
31 import javax.xml.XMLConstants JavaDoc;
32 import javax.xml.namespace.QName JavaDoc;
33 import org.netbeans.modules.xml.xam.AbstractComponent;
34 import org.netbeans.modules.xml.xam.EmbeddableRoot;
35 import org.netbeans.modules.xml.xam.locator.CatalogModel;
36 import org.netbeans.modules.xml.xam.locator.CatalogModelException;
37 import org.netbeans.modules.xml.xam.ModelSource;
38 import org.w3c.dom.Document JavaDoc;
39 import org.w3c.dom.Node JavaDoc;
40 import org.w3c.dom.Element JavaDoc;
41 import org.w3c.dom.NamedNodeMap JavaDoc;
42 import org.w3c.dom.NodeList JavaDoc;
43 import org.w3c.dom.Text JavaDoc;
44
45 /**
46  *
47  * @author rico
48  * @author Vidhya Narayanan
49  * @author Chris Webster
50  * @author Nam Nguyen
51  */

52 public abstract class AbstractDocumentComponent<C extends DocumentComponent<C>>
53         extends AbstractComponent<C> implements DocumentComponent<C>, DocumentModelAccess.NodeUpdater {
54     private Element JavaDoc node;
55     
56     protected abstract void populateChildren(List JavaDoc<C> children);
57     
58     public AbstractDocumentComponent(AbstractDocumentModel model, org.w3c.dom.Element JavaDoc e) {
59         super(model);
60         setRef(e);
61     }
62     
63     /**
64      * Stores the reference to the DOM node
65      */

66     private void setRef(Element JavaDoc n) {
67         assert n != null : "n must not be null";
68         node = n;
69     }
70     
71     public synchronized Element JavaDoc getPeer() {
72         return node;
73     }
74     
75     /**
76      * @return attribute value or null if the attribute is currently undefined
77      */

78     public String JavaDoc getAttribute(Attribute attr) {
79         return normalizeUndefinedAttributeValue(
80         getPeer().getAttribute(attr.getName()));
81     }
82     
83     /**
84      * Sets the component attribute String value and fire property change event
85      * with the given property name.
86      *
87      * @param eventPropertyName property name to be used in firing property change event.
88      * @param attr attribute name
89      * @value attribute value
90      */

91     public void setAttribute(String JavaDoc eventPropertyName, Attribute attr, Object JavaDoc value) {
92         verifyWrite();
93         Object JavaDoc old = null;
94         String JavaDoc s = getAttribute(attr);
95         if (s != null) {
96             try {
97                 old = getAttributeValueOf(attr, s);
98             } catch(IllegalArgumentException JavaDoc ex) {
99                 //ignored, equivalent to having no old value
100
}
101         }
102         setAttributeQuietly(attr, value);
103         firePropertyChange(eventPropertyName, old, value);
104         fireValueChanged();
105     }
106     
107     abstract protected Object JavaDoc getAttributeValueOf(Attribute attr, String JavaDoc stringValue);
108     
109     /**
110      * Returns string value of the attribute from different namespace.
111      * If given QName has prefix, it will be ignored.
112      * @param attr non-null QName represents the attribute name.
113      * @return attribute value
114      */

115     public String JavaDoc getAnyAttribute(QName JavaDoc attr) {
116         assert attr != null;
117         String JavaDoc name = attr.getLocalPart();
118         String JavaDoc namespace = attr.getNamespaceURI();
119         String JavaDoc prefix = namespace == null ? null : lookupPrefix(namespace);
120         String JavaDoc attrName = prefix == null ? name : prefix + ":" + name; //NOI18N
121
return normalizeUndefinedAttributeValue(
122         getPeer().getAttribute(attrName));
123     }
124     
125     /**
126      * use the normalized value method from ModelAccess if available, otherwise
127      * just return the attribute value. The normalized access will not be
128      * available if the component has been deleted from the model.
129      */

130     private String JavaDoc normalizeUndefinedAttributeValue(String JavaDoc value) {
131     String JavaDoc normalizedValue = value;
132     if (getModel() != null) {
133         normalizedValue =
134         getAccess().normalizeUndefinedAttributeValue(value);
135     }
136     return normalizedValue;
137     }
138     
139     /**
140      * Set string value of the attribute identified by given QName.
141      * This will fire property change event using attribute local name.
142      * @param attr non-null QName represents the attribute name.
143      * @param value string value for the attribute.
144      */

145     public void setAnyAttribute(QName JavaDoc attr, String JavaDoc value) {
146         setQNameAttribute(attr.getLocalPart(), attr, value);
147     }
148     
149     protected void setQNameAttribute(String JavaDoc propertyName, QName JavaDoc attr, String JavaDoc value) {
150         assert attr != null;
151
152         verifyWrite();
153         
154         String JavaDoc name = getPrefixedName(attr, (value != null));
155         String JavaDoc old = getAnyAttribute(attr);
156         if (value == null) {
157             removeAttribute(getPeer(), name);
158         } else {
159             setAttribute(getPeer(), name, value);
160         }
161         
162         firePropertyChange(propertyName, old, value);
163         fireValueChanged();
164     }
165     
166     protected String JavaDoc getPrefixedName(QName JavaDoc q, boolean declarePrefix) {
167         String JavaDoc name = q.getLocalPart();
168         String JavaDoc namespace = q.getNamespaceURI();
169         String JavaDoc prefix = q.getPrefix();
170         return getPrefixedName(namespace, name, prefix, declarePrefix);
171     }
172     
173
174     protected String JavaDoc getPrefixedName(String JavaDoc namespace, String JavaDoc localName) {
175         return getPrefixedName(namespace, localName, null, false);
176     }
177     
178     protected String JavaDoc getPrefixedName(String JavaDoc namespace, String JavaDoc name, String JavaDoc prefix, boolean declarePrefix) {
179         if (namespace == null || namespace.length() == 0) {
180             declarePrefix = false;
181         }
182         String JavaDoc existingPrefix = lookupPrefix(namespace);
183         AbstractDocumentComponent root = (AbstractDocumentComponent)getModel().getRootComponent();
184         if (existingPrefix == null) {
185             //might have not been added to xmd tree, so lookup at tree root
186
existingPrefix = root.lookupPrefix(namespace);
187         }
188         //recheck to see if this prefix is overridden
189
if (existingPrefix != null) {
190             String JavaDoc localNS = lookupNamespaceURI(existingPrefix);
191             if (localNS != null && ! localNS.equals(namespace)) {
192                 existingPrefix = null;
193             }
194         }
195             
196         if (existingPrefix != null) {
197             prefix = existingPrefix;
198         } else if (declarePrefix) {
199             if (prefix == null) {
200                 prefix = "ns"; //NOI18N
201
}
202             if (prefix.length() > 0) {
203                 prefix = root.ensureUnique(prefix, namespace);
204             }
205             if (isInDocumentModel()) {
206                 root.addPrefix(prefix, namespace);
207             } else {
208                 addPrefix(prefix, namespace);
209             }
210         }
211         
212         if (prefix != null && prefix.length() > 0) {
213             name = prefix + ":" + name; //NOI18N
214
}
215         
216         return name;
217     }
218     
219     /**
220      * Returns a unique prefix for the given namespace by appending number from 1 to 100.
221      */

222     protected String JavaDoc ensureUnique(String JavaDoc prefix, String JavaDoc namespace) {
223         assert namespace != null;
224         int count = 0;
225         String JavaDoc prefixN = prefix;
226         String JavaDoc existing = lookupNamespaceURI(prefixN);
227         while (existing != null && count < 100 && ! existing.equals(namespace)) {
228             ++count;
229             prefixN = prefix+count;
230             existing = lookupNamespaceURI(prefix+count);
231         }
232         if (count >= 100) {
233             Logger.getLogger(getClass().getName()).log(
234                 Level.FINE, "Failed to generate unique prefix for "+namespace); //NOI18N
235
}
236         return prefixN;
237     }
238     
239     protected void setAttributeQuietly(Attribute type, Object JavaDoc newVal) {
240         if (newVal == null) {
241             removeAttribute(node, type.getName());
242         } else {
243             String JavaDoc stringValue = newVal.toString();
244             if (newVal instanceof NamedComponentReference) {
245                 NamedComponentReference ref = (NamedComponentReference) newVal;
246                 QName JavaDoc q = ref.getQName();
247                 stringValue = getPrefixedName(q.getNamespaceURI(), q.getLocalPart(), null, true);
248                 if (getEffectiveParent() == null) {
249                     Logger.getLogger(getClass().getName()).log(Level.FINE,
250                             "Referencing while not in tree yet could result in unwanted prefix declaration"); //NOI18N
251
}
252                 ((AbstractNamedComponentReference) ref).refresh();
253             }
254             setAttribute(node, type.getName(), stringValue);
255         }
256     }
257     
258     protected void removeAttributeQuietly(Element JavaDoc element, String JavaDoc name) {
259         getAccess().removeAttribute(element, name, this);
260     }
261     
262     protected void appendChildQuietly(C component, List JavaDoc<C> children) {
263         fixupPrefix(component);
264         getAccess().appendChild(getPeer(), component.getPeer(), this);
265         children.add(component);
266     }
267     
268     protected void insertAtIndexQuietly(C newComponent, List JavaDoc<C> children, int index) {
269         if (index >= 0 && children.size() > 0 && index < children.size()) {
270             fixupPrefix(newComponent);
271             Node JavaDoc refChild = children.get(index).getPeer();
272             insertBefore(newComponent.getPeer(), refChild);
273             children.add(index, newComponent);
274         } else {
275             appendChildQuietly(newComponent, children);
276         }
277     }
278     
279     protected void removeChildQuietly(C component, List JavaDoc<C> children) {
280         removeChild(component.getPeer());
281         children.remove(component);
282     }
283     
284     protected String JavaDoc getNamespaceURI() {
285         String JavaDoc ns = getPeer().getNamespaceURI();
286         if (ns == null && getEffectiveParent() != null) {
287             String JavaDoc prefix = getPeer().getPrefix();
288             ns = lookupNamespaceURI(prefix);
289         }
290         return ns;
291     }
292
293     /**
294      * Returns namespace for the given prefix. If optimized is specified,
295      * lookup will use component tree hierarchy instead of the underlying DOM tree hiearchy.
296      */

297     public String JavaDoc lookupNamespaceURI(String JavaDoc prefix, boolean optimized) {
298         if (optimized) {
299             String JavaDoc namespace = getPrefixes().get(prefix == null ? "" : prefix);
300             if (namespace == null && getEffectiveParent() != null) {
301                 namespace = getEffectiveParent().lookupNamespaceURI(prefix, true);
302             }
303             return namespace;
304         } else {
305             return lookupNamespaceURI(prefix);
306         }
307     }
308     
309     public String JavaDoc lookupNamespaceURI(String JavaDoc prefix) {
310         String JavaDoc ns = getPeer().lookupNamespaceURI(prefix);
311         if (ns == null && getEffectiveParent() != null) {
312             ns = getEffectiveParent().lookupNamespaceURI(prefix);
313         }
314         return ns;
315     }
316     
317     public String JavaDoc lookupPrefix(String JavaDoc namespace){
318         String JavaDoc prefix = getPeer().lookupPrefix(namespace);
319         if (prefix == null && getEffectiveParent() != null) {
320             prefix = getEffectiveParent().lookupPrefix(namespace);
321         }
322         return prefix;
323     }
324     
325     /**
326      * Get the XML fragment text that make up the children the component peer node.
327      */

328     protected String JavaDoc getXmlFragment() {
329         return getAccess().getXmlFragment(getPeer());
330     }
331     
332     /**
333      * Get the XML fragment text that make up the peer node.
334      */

335     public String JavaDoc getXmlFragmentInclusive() {
336         return getModel().getAccess().getXmlFragmentInclusive(getPeer());
337     }
338     
339     /**
340      * Set text as XML fragment children the component peer node.
341      * The children of peer node will be replaced with nodes resulted from
342      * parsing given text. This method will not fire child component added
343      * or removed events. This method should only be exposed on leaf component.
344      *
345      * @param propertyName name of property event to fire
346      * @param text text value to set to.
347      * @exception IOException if text is not well-formed.
348      */

349     protected synchronized void setXmlFragment(String JavaDoc propertyName, String JavaDoc text) throws IOException JavaDoc {
350         verifyWrite();
351         String JavaDoc oldVal = getText();
352         getAccess().setXmlFragment(getPeer(), text, this);
353         firePropertyChange(propertyName, oldVal, text);
354         fireValueChanged();
355     }
356     
357     /**
358      * Set text value of the component. This is for pure text-usage by documentation
359      * components. The children of peer node will be replaced with single
360      * text node having given text.
361      * @param propertyName name of property event to fire
362      * @param text text value to set to.
363      */

364     protected synchronized void setText(String JavaDoc propertyName, String JavaDoc text) {
365         verifyWrite();
366         String JavaDoc oldVal = getText();
367         getAccess().setText(getPeer(), text, this);
368         firePropertyChange(propertyName, oldVal, text);
369         fireValueChanged();
370     }
371     
372     /**
373      * Return text value of this component. This is for text-usage by doucmentation
374      * components. Non-text children node are ignored.
375      * @return aggregated text string of all child text nodes.
376      */

377     protected String JavaDoc getText() {
378         return getText(getPeer());
379     }
380
381     public static String JavaDoc getText(Element JavaDoc e) {
382         StringBuilder JavaDoc text = new StringBuilder JavaDoc();
383         org.w3c.dom.NodeList JavaDoc nl = e.getChildNodes();
384         for (int i = 0; i < nl.getLength(); i++) {
385             org.w3c.dom.Node JavaDoc n = nl.item(i);
386             if (n instanceof org.w3c.dom.Text JavaDoc) {
387                 text.append(n.getNodeValue());
388             }
389         }
390         return text.toString();
391     }
392     
393     public AbstractDocumentModel getModel() {
394         return (AbstractDocumentModel) super.getModel();
395     }
396     
397     public boolean referencesSameNode(Node JavaDoc n) {
398         return getModel().areSameNodes(getPeer(), n);
399     }
400     
401     public synchronized void updateReference(Element JavaDoc element) {
402         node = element;
403     }
404
405     /**
406      * Update all parents with fresh nodes.
407      * This implemenation assume optimal case where the length of pathToRootNode
408      * is same as or longer than path to root component. The update will skip
409      * uncorrelated nodes in pathToRootNode. Individual will need to override
410      * this implementation if it has special needs.
411      */

412     public synchronized <N extends Node JavaDoc> void updateReference(List JavaDoc<N> pathToRoot) {
413         AbstractDocumentComponent current = this;
414         assert pathToRoot != null && pathToRoot.size() > 0;
415         for (int i=0; i<pathToRoot.size(); i++) {
416             assert pathToRoot.get(i) instanceof Element JavaDoc;
417             Element JavaDoc e = (Element JavaDoc) pathToRoot.get(i);
418             if (current.referencesSameNode(e)) {
419                 current.updateReference(e, pathToRoot);
420                 if (current.getEffectiveParent() != null) {
421                     current = (AbstractDocumentComponent) current.getEffectiveParent();
422                 } else {
423                     break;
424                 }
425             } else if (i == pathToRoot.size()-1) {
426                 throw new IllegalArgumentException JavaDoc("Expect new reference node has same Id as current"); //NOI18N
427
}
428         }
429     }
430     
431     /**
432      * Updates peer node with given peer and the path for context of the update.
433      * The default behavior just call #updateReference(Element peer).
434      * Subclass with special need for auxiliary update needs to override.
435      *
436      * @param peer the peer node to update with
437      * @param updatingPath full path for context of the update
438      */

439     protected <N extends Node JavaDoc> void updateReference(Element JavaDoc peer, List JavaDoc<N> updatingPath) {
440         updateReference(peer);
441     }
442     
443     protected DocumentModelAccess getAccess() {
444         getChildren(); //make sure children populated before potential mutation
445
return (DocumentModelAccess) getModel().getAccess();
446     }
447     
448     public int findPosition() {
449         if (getModel() == null) {
450             return 0;
451         }
452         return getAccess().findPosition(getPeer());
453     }
454     
455     private void removeAttribute(Element JavaDoc element, String JavaDoc name) {
456         getAccess().removeAttribute(element, name, this);
457     }
458     
459     private void setAttribute(Element JavaDoc element, String JavaDoc name, String JavaDoc value) {
460         getAccess().setAttribute(element, name, value, this);
461     }
462     
463     private void insertBefore(Node JavaDoc newChild, Node JavaDoc refChild) {
464         getAccess().insertBefore(node, newChild, refChild, this);
465     }
466     
467     private void removeChild(Node JavaDoc child) {
468         getAccess().removeChild(node, child, this);
469     }
470     
471     /**
472      * Shared utility for implementation to replace the current peer and
473      * ensure the document tree also get update properly.
474      */

475     protected void updatePeer(String JavaDoc propertyName, org.w3c.dom.Element JavaDoc newPeer) {
476         AbstractDocumentComponent aParent = getEffectiveParent();
477         Element JavaDoc parentPeer = aParent.getPeer();
478         Element JavaDoc oldPeer = getPeer();
479         getAccess().replaceChild(parentPeer, getPeer(), newPeer, aParent);
480         updateReference(newPeer);
481         firePropertyChange(propertyName, oldPeer, newPeer);
482         fireValueChanged();
483     }
484     
485     protected Attribute createPrefixAttribute(String JavaDoc prefix) {
486         assert prefix != null;
487         if (prefix.length() == 0) {
488             return new PrefixAttribute(XMLConstants.XMLNS_ATTRIBUTE);
489         } else {
490             return new PrefixAttribute("xmlns:"+prefix); //NOI18N
491
}
492     }
493     
494     /**
495      * Declare prefix for given namespace (without any refactoring action).
496      */

497     public void addPrefix(String JavaDoc prefix, String JavaDoc namespace) {
498         Attribute a = createPrefixAttribute(prefix);
499         setAttribute(a.getName(), a, namespace);
500     }
501     
502     /**
503      * Remove declared prefix (without refactoring).
504      */

505     public void removePrefix(String JavaDoc prefix) {
506         setAttribute(prefix, createPrefixAttribute(prefix), null);
507     }
508     
509     /**
510      * @return mapping from prefix to namespace.
511      */

512     public Map JavaDoc<String JavaDoc, String JavaDoc> getPrefixes() {
513         Map JavaDoc<String JavaDoc,String JavaDoc> prefixes = new HashMap JavaDoc<String JavaDoc,String JavaDoc>();
514         NamedNodeMap JavaDoc nodes = getPeer().getAttributes();
515         for (int i = 0; i < nodes.getLength(); i++) {
516             Node JavaDoc n = nodes.item(i);
517             String JavaDoc name = n.getLocalName();
518         String JavaDoc prefix = n.getPrefix();
519         final String JavaDoc xmlns = XMLConstants.XMLNS_ATTRIBUTE; //NOI18N
520
if (xmlns.equals(name) || // default namespace
521
xmlns.equals(prefix)) { // namespace prefix
522
String JavaDoc ns = n.getNodeValue();
523         prefixes.put(name, ns);
524         }
525         }
526         String JavaDoc defaultNamespace = prefixes.remove(XMLConstants.XMLNS_ATTRIBUTE);
527         if (defaultNamespace != null) {
528             prefixes.put(XMLConstants.DEFAULT_NS_PREFIX, defaultNamespace);
529         }
530         return prefixes;
531     }
532     
533     public static class PrefixAttribute implements Attribute {
534         private String JavaDoc prefix;
535         public PrefixAttribute(String JavaDoc name) {
536             prefix = name;
537         }
538         public Class JavaDoc getType() { return String JavaDoc.class; }
539         public String JavaDoc getName() { return prefix; }
540         public Class JavaDoc getMemberType() { return null; }
541     }
542     
543     /**
544      * Ensure (recursively) children prefix is same as parent if same namespace.
545      * To be used when adding newcomponent subtree to this component.
546      */

547     private void fixupPrefix(C newComponent) {
548         if (getModel().inSync()) return;
549         
550         AbstractDocumentComponent child = (AbstractDocumentComponent) newComponent;
551         Element JavaDoc e = child.getPeer();
552         String JavaDoc childNS = child.getNamespaceURI();
553         if (childNS == null || childNS.equals(XMLConstants.NULL_NS_URI)) {
554             return;
555         }
556        
557         if (childNS.equals(getNamespaceURI())) {
558             e.setPrefix(getPeer().getPrefix());
559         } else if (childNS.equals(lookupNamespaceURI(""))) {
560             e.setPrefix(null);
561         } else {
562             ensurePrefixDeclaredFor(e, childNS);
563         }
564
565         for (C c : newComponent.getChildren()) {
566             fixupPrefix(c);
567         }
568     }
569
570     private void ensurePrefixDeclaredFor(Element JavaDoc newComponentElement, String JavaDoc newComponentNS) {
571         String JavaDoc existingPrefix = lookupPrefix(newComponentNS);
572         String JavaDoc prefix = newComponentElement.getPrefix();
573         if (existingPrefix == null) {
574             if (prefix == null) {
575                 prefix = "ns"; //NOI18N
576
}
577             prefix = ensureUnique(prefix, newComponentNS);
578             ((AbstractDocumentComponent)getModel().getRootComponent()).addPrefix(prefix, newComponentNS);
579             newComponentElement.setPrefix(prefix);
580         } else {
581             newComponentElement.setPrefix(existingPrefix);
582         }
583     }
584     
585     protected void ensureValueNamespaceDeclared(String JavaDoc newNamespace, String JavaDoc oldNamespace,
586             String JavaDoc preferredPrefix) {
587         if (newNamespace == null) return;
588         String JavaDoc prefix = null;
589         if (oldNamespace != null) {
590             prefix = lookupPrefix(oldNamespace);
591         }
592         
593         if (prefix == null) {
594             // see if i can use 'tns'
595
String JavaDoc tnsURI = lookupNamespaceURI(preferredPrefix);
596             if (tnsURI == null) {
597                 prefix = preferredPrefix;
598             } else {
599                 prefix = ensureUnique(preferredPrefix, newNamespace);
600             }
601             addPrefix(prefix, newNamespace);
602         } else { // redefined existing prefix
603
removePrefix(prefix);
604             addPrefix(prefix, newNamespace);
605         }
606     }
607     
608     public C findChildComponent(Element JavaDoc e) {
609         for (C c : getChildren()) {
610             if (c.referencesSameNode(e)) {
611                 return c;
612             }
613         }
614         return null;
615     }
616
617     public C findChildComponentByIdentity(Element JavaDoc e) {
618         ElementIdentity ei = getModel().getAccess().getElementIdentity();
619         Document JavaDoc doc = getModel().getDocument();
620         for (C c : getChildren()) {
621             if (ei.compareElement(c.getPeer(), e, doc, doc)) {
622                 return c;
623             }
624         }
625         return null;
626     }
627     
628     public DocumentComponent copy(C parent) {
629         if (getModel() == null) {
630             throw new IllegalStateException JavaDoc("Cannot copy component already removed from model");
631         }
632          Element JavaDoc newPeer = getAccess().duplicate(getPeer());
633         DocumentModel<C> m = parent == null ? getModel() : (DocumentModel) parent.getModel();
634         return m.createComponent(parent, newPeer);
635     }
636     
637     protected void verifyWrite() {
638         if (getModel() == null) {
639             throw new IllegalStateException JavaDoc("Cannot mutate a component already removed from model.");
640         }
641         if (isInDocumentModel()) {
642             getModel().validateWrite();
643         }
644     }
645     
646     protected void firePropertyChange(String JavaDoc propName, Object JavaDoc oldValue, Object JavaDoc newValue) {
647         if (isInDocumentModel()) {
648             super.firePropertyChange(propName, oldValue, newValue);
649         }
650     }
651     
652     protected void fireValueChanged() {
653         if (isInDocumentModel()) {
654             super.fireValueChanged();
655         }
656     }
657     
658     protected void fireChildRemoved() {
659         if (isInDocumentModel()) {
660             super.fireChildRemoved();
661         }
662     }
663     
664     protected void fireChildAdded() {
665         if (isInDocumentModel()) {
666             super.fireChildAdded();
667         }
668     }
669     
670     /**
671      * Returns true if the component is part of the document model.
672      */

673     public boolean isInDocumentModel() {
674         if (getModel() == null) return false;
675         AbstractDocumentComponent root = (AbstractDocumentComponent) getModel().getRootComponent();
676         if (root == null) return false;
677         if (root == this) return true;
678         AbstractDocumentComponent myRoot = (AbstractDocumentComponent) getEffectiveParent();
679         if (myRoot == null) return false; // no parent
680
while (myRoot != null && myRoot.getEffectiveParent() != null) {
681             if (myRoot instanceof EmbeddableRoot) {
682                 root = (AbstractDocumentComponent) myRoot.getEffectiveParent().getModel().getRootComponent();
683             }
684             myRoot = (AbstractDocumentComponent) myRoot.getEffectiveParent();
685         }
686         return root == myRoot;
687     }
688     
689     public int findAttributePosition(String JavaDoc attributeName) {
690         org.w3c.dom.Attr JavaDoc a = getPeer().getAttributeNode(attributeName);
691         if (a != null) {
692             return getAccess().findPosition(a);
693         } else {
694             return -1;
695         }
696     }
697     
698     /**
699      * Returns QName of the component.
700      */

701     public QName JavaDoc getQName() {
702         return getQName(getPeer());
703     }
704     
705     public static QName JavaDoc getQName(Node JavaDoc n) {
706         String JavaDoc namespace = n.getNamespaceURI();
707         String JavaDoc localName = n.getLocalName();
708         String JavaDoc prefix = n.getPrefix();
709         assert(localName != null);
710         if (namespace == null && prefix == null) {
711             return new QName JavaDoc(localName);
712         } else if (namespace != null && prefix == null) {
713             return new QName JavaDoc(namespace, localName);
714         } else {
715             return new QName JavaDoc(namespace, localName, prefix);
716         }
717     }
718     
719     private ModelSource resolveModelSource(String JavaDoc location,
720     ModelSource currentSource, CatalogModel currentCatalog) {
721     ModelSource ms = null;
722     try {
723         if (location != null) {
724         ms = currentCatalog.getModelSource(getURI(location),
725             currentSource);
726         }
727     } catch (CatalogModelException nse) {
728         // unable to resolve location
729
Logger JavaDoc l = Logger.getLogger(AbstractDocumentComponent.class.getName());
730         l.log(Level.FINE, nse.getMessage());
731     }
732     return ms;
733     }
734     
735     /**
736      * Resolves reference to external models using location hint.
737      * @param hint on location of where external model reference could reside.
738      * @return the model source of the referenced model if found.
739      * @throws CatalogModelException if the model cannot be located or the
740      * hint is not well-formed URI
741      */

742     protected ModelSource resolveModel(String JavaDoc hint) throws CatalogModelException {
743         return _resolveModel(hint, null);
744     }
745     
746     private ModelSource _resolveModel(String JavaDoc hint, String JavaDoc backup) throws CatalogModelException {
747     CatalogModel nr = (CatalogModel)
748         getModel().getModelSource().getLookup().lookup(CatalogModel.class);
749
750     // try hint
751
ModelSource ms = resolveModelSource(hint, getModel().getModelSource(),
752         nr);
753         
754     // hint didn't work now try backup
755
if (ms == null) {
756         ms = resolveModelSource(backup, getModel().getModelSource(),
757         nr);
758     }
759     
760     // unable to resolve
761
if (ms == null) {
762             String JavaDoc msg = "Cannot resolve file using hint = " + hint + //NOI18N
763
" backup = " + backup; //NOI18N
764
throw new CatalogModelException(msg);
765         }
766     
767     return ms;
768     }
769     
770     private static URI JavaDoc getURI(String JavaDoc s) throws CatalogModelException {
771     try {
772         return new URI JavaDoc(s);
773     } catch (URISyntaxException JavaDoc ex) {
774         throw new CatalogModelException(ex);
775     }
776     }
777     
778     public Map JavaDoc<QName JavaDoc,String JavaDoc> getAttributeMap() {
779         return getModel().getAccess().getAttributeMap(getPeer());
780     }
781     
782     /**
783      * @returns the index of given element relative to other domain elements in
784      * the peer node children; return -1 if not domain element or not found.
785      */

786     protected int findDomainIndex(Element JavaDoc e) {
787         if (! getModel().isDomainElement(e)) {
788             return -1;
789         }
790         int domainInsertIndex = 0;
791         NodeList JavaDoc nl = getPeer().getChildNodes();
792         for (int i=0; i<nl.getLength(); i++) {
793             if (nl.item(i) == e) {
794                 return domainInsertIndex;
795             }
796             if (getModel().isDomainElement(nl.item(i))) {
797                 domainInsertIndex++;
798             }
799         }
800         return -1;
801     }
802     
803     protected AbstractDocumentComponent getEffectiveParent() {
804         AbstractDocumentComponent p = (AbstractDocumentComponent) getParent();
805         if (p == null && this instanceof EmbeddableRoot) {
806             p = (AbstractDocumentComponent) ((EmbeddableRoot)this).getForeignParent();
807         }
808         return p;
809     }
810
811
812     protected Element JavaDoc getChildElement(QName JavaDoc qname) {
813         NodeList JavaDoc nl = getPeer().getElementsByTagName(qname.getLocalPart());
814         Element JavaDoc ret = null;
815         if (nl != null) {
816             for (int i=0; i<nl.getLength(); i++) {
817                 if (qname.equals(getQName(nl.item(i)))) {
818                     ret = (Element JavaDoc) nl.item(i);
819                     break;
820                 }
821             }
822         }
823         return ret;
824     }
825     
826     /**
827      * Returns value of all text nodes from the child element with given QName.
828      * This method is use to implement mapping of "property" as component attribute.
829      * @param qname QName of the child element to get text from.
830      */

831     protected String JavaDoc getChildElementText(QName JavaDoc qname) {
832         Element JavaDoc ret = getChildElement(qname);
833         return ret == null ? null : getText(ret);
834     }
835     
836     /**
837      * Set the value of the text node from the child element with given QName.
838      * This method is use to implement mapping of "property" as component attribute.
839      * @param propertyName property change event name
840      * @param text the string to set value of the child element text node.
841      * @param qname QName of the child element to get text from.
842      */

843     protected void setChildElementText(String JavaDoc propertyName, String JavaDoc text, QName JavaDoc qname) {
844         verifyWrite();
845         Element JavaDoc childElement = getChildElement(qname);
846         String JavaDoc oldVal = childElement == null ? null : getText(childElement);
847         
848         if (text == null) {
849             if (childElement == null) return;
850             removeChild(childElement);
851             if (oldVal == null) return;
852         } else if (text.length() == 0) {
853             if (childElement != null) {
854                 removeChild(childElement);
855             }
856             childElement = getModel().getDocument().createElementNS(qname.getNamespaceURI(), qname.getLocalPart());
857             getModel().getAccess().appendChild(getPeer(), childElement, this);
858         } else {
859             if (text.equals(oldVal)) return;
860             if (childElement == null) {
861                 childElement = getModel().getDocument().createElementNS(qname.getNamespaceURI(), qname.getLocalPart());
862                 getModel().getAccess().appendChild(getPeer(), childElement, this);
863             }
864             getModel().getAccess().setText(childElement, text, this);
865         }
866         firePropertyChange(propertyName, oldVal, text);
867         fireValueChanged();
868     }
869
870     /**
871      * Returns leading text for the child component of the given index.
872      * @param child the child to get associated text from
873      * @return value of the leading text node, or null the indexed component peer does not have leading text nodes.
874      */

875     protected String JavaDoc getLeadingText(C child) {
876         return getText(child, true, true);
877     }
878     
879     /**
880      * Set leading text for the child component which position is the given index.
881      * @param child the child to set associated text
882      * @param text value of the leading text node, or null to remove the leading text node.
883      */

884     protected void setLeadingText(String JavaDoc propName, String JavaDoc text, C child) {
885         setText(propName, text, child, true, true);
886     }
887     
888     /**
889      * Returns trailing text for the child component of the given index.
890      * @param child the child to get associated text from
891      * @return value of the leading text node, or null the indexed component peer does not have trailing text nodes.
892      */

893     protected String JavaDoc getTrailingText(C child) {
894         return getText(child, false, true);
895     }
896     
897     /**
898      * Set trailing text for the child component which position is the given index.
899      * @param child the child to get associated text from
900      * @param text value of the trailing text node, or null to remove the trailing text node.
901      */

902     protected void setTrailingText(String JavaDoc propName, String JavaDoc text, C child) {
903         setText(propName, text, child, false, true);
904     }
905     
906     protected String JavaDoc getText(C child, boolean leading, boolean includeComments) {
907         int domIndex = getNodeIndexOf(getPeer(), child.getPeer());
908         if (domIndex < 0) {
909             throw new IllegalArgumentException JavaDoc("Child peer node is not part of children nodes");
910         }
911         
912         StringBuilder JavaDoc value = null;
913         NodeList JavaDoc nl = getPeer().getChildNodes();
914         
915         for (int i = (leading ? domIndex-1 : domIndex+1);
916              i > -1 && i < nl.getLength(); i = leading ? --i : ++i)
917         {
918             Node JavaDoc n = nl.item(i);
919             if (n instanceof Element JavaDoc) {
920                 break;
921             }
922
923             if (n instanceof Text JavaDoc && (includeComments || n.getNodeType() != Node.COMMENT_NODE)) {
924                 if (value == null) value = new StringBuilder JavaDoc();
925                 if (leading) {
926                     value.insert(0, n.getNodeValue());
927                 } else {
928                     value.append(n.getNodeValue());
929                 }
930             }
931         }
932         return value == null ? null : value.toString();
933     }
934     
935     protected void setText(String JavaDoc propName, String JavaDoc value, C child, final boolean leading, boolean includeComments) {
936         verifyWrite();
937         StringBuilder JavaDoc oldValue = null;
938         ArrayList JavaDoc<Node JavaDoc> toRemove = new ArrayList JavaDoc<Node JavaDoc>();
939         NodeList JavaDoc nl = getPeer().getChildNodes();
940         int domIndex = getNodeIndexOf(getPeer(), child.getPeer());
941         if (domIndex < 0) {
942             throw new IllegalArgumentException JavaDoc("Child peer node is not part of children nodes");
943         }
944
945         Element JavaDoc ref = leading ? child.getPeer() : null;
946         for (int i = leading ? domIndex-1 : domIndex+1;
947              i > -1 && i < nl.getLength(); i = leading ? --i : ++i)
948         {
949             Node JavaDoc n = nl.item(i);
950             if (n != null && n.getNodeType() == Node.ELEMENT_NODE) {
951                 if (leading) {
952                     ref = child.getPeer();
953                 } else {
954                     ref = (Element JavaDoc) n;
955                 }
956                 break;
957             }
958             if (n instanceof Text JavaDoc && (includeComments || n.getNodeType() != Node.COMMENT_NODE)) {
959                 toRemove.add(n);
960                 if (oldValue == null) oldValue = new StringBuilder JavaDoc();
961                 if (leading) {
962                     oldValue.insert(0, n.getNodeValue());
963                 } else {
964                     oldValue.append(n.getNodeValue());
965                 }
966             }
967         }
968         
969         getModel().getAccess().removeChildren(getPeer(), toRemove, this);
970         if (value != null) {
971              Text JavaDoc newNode = getModel().getDocument().createTextNode(value);
972              if (ref != null) {
973                 getModel().getAccess().insertBefore(getPeer(), newNode, ref, this);
974              } else {
975                 getModel().getAccess().appendChild(getPeer(), newNode, this);
976              }
977         }
978         
979         firePropertyChange(propName, oldValue == null ? null : oldValue.toString(), value);
980         fireValueChanged();
981     }
982     
983     protected int getNodeIndexOf(Node JavaDoc parent, Node JavaDoc child) {
984         if (child == null) {
985             return -1;
986         }
987         int nodeIndex = -1;
988         for (int i = 0; i < parent.getChildNodes().getLength(); i++) {
989             Node JavaDoc n = parent.getChildNodes().item(i);
990             nodeIndex++;
991             if (getAccess().areSameNodes(n, child)) {
992                 return nodeIndex;
993             }
994         }
995         return -1;
996     }
997
998 }
999
1000
Popular Tags