KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > xml > axi > AXIComponent


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-2007 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.netbeans.modules.xml.axi;
20
21 import java.beans.PropertyChangeEvent JavaDoc;
22 import java.beans.PropertyChangeListener JavaDoc;
23 import java.beans.PropertyChangeSupport JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.Arrays JavaDoc;
26 import java.util.Collections JavaDoc;
27 import java.util.List JavaDoc;
28 import java.util.Set JavaDoc;
29 import java.util.WeakHashMap JavaDoc;
30 import org.netbeans.modules.xml.axi.Compositor.CompositorType;
31 import org.netbeans.modules.xml.axi.impl.AXIDocumentImpl;
32 import org.netbeans.modules.xml.axi.impl.AXIModelBuilder;
33 import org.netbeans.modules.xml.axi.impl.AXIModelImpl;
34 import org.netbeans.modules.xml.axi.impl.Util;
35 import org.netbeans.modules.xml.axi.visitor.AXIVisitor;
36 import org.netbeans.modules.xml.schema.model.Documentation;
37 import org.netbeans.modules.xml.schema.model.SchemaComponent;
38 import org.netbeans.modules.xml.xam.AbstractComponent;
39 import org.openide.util.WeakListeners;
40
41 /**
42  * Base component in the AXI model tree.
43  *
44  * @author Samaresh (Samaresh.Panda@Sun.Com)
45  */

46 public abstract class AXIComponent extends AbstractComponent<AXIComponent>
47         implements Cloneable JavaDoc, PropertyChangeListener JavaDoc {
48     
49     /**
50      * Represents the type of this component.
51      * Can be one of local, shared, proxy or reference.
52      */

53     public static enum ComponentType {
54         LOCAL,
55         SHARED,
56         PROXY,
57         REFERENCE
58     }
59     
60     /**
61      * Creates a new instance of AXIComponent.
62      */

63     public AXIComponent(AXIModel model) {
64         super(model);
65     }
66     
67     /**
68      * Creates a new instance of AXIComponent.
69      */

70     public AXIComponent(AXIModel model, SchemaComponent schemaComponent) {
71         super(model);
72         setPeer(schemaComponent);
73     }
74     
75     /**
76      * Creates a proxy component for the specified global or shared component.
77      */

78     public AXIComponent(AXIModel model, AXIComponent sharedComponent) {
79         super(model);
80         setSharedComponent(sharedComponent);
81     }
82
83     /**
84      * Allow an AXIVisitor to visit this component.
85      */

86     public abstract void accept(AXIVisitor visitor);
87     
88     
89     /**
90      * Returns this component's absolute index in the parent's children list.
91      * Returns -1 if child or parent are not in model or the child is
92      * not found in the parent's children list.
93      */

94     public int getIndex() {
95         return getIndex(true);
96     }
97     
98     /**
99      * Returns this component's index (relative or absolute) in the parent's children list.
100      * Returns -1 if child or parent are not in model or the child is
101      * not found in the parent's children list.
102      *
103      * @param absolute - true, relative (to its type in parent) - false
104      */

105     public int getIndex(boolean absolute) {
106         AXIComponent parent = getParent();
107         if(parent == null || !isInModel())
108             return -1;
109         List JavaDoc<AXIComponent> childs = Collections.emptyList();
110         if(absolute)
111             childs = parent.getChildren();
112         else
113             childs = parent.getChildren((Class JavaDoc<AXIComponent>)this.getClass());
114         for(int i=0; i<childs.size(); i++) {
115             if(childs.get(i) == this) {
116                 return i;
117             }
118         }
119         return -1;
120     }
121     
122     /**
123      * In AXI model a proxy acts on behalf of a sharable or global component.
124      * However, there can be multiple levels of indirection to the original global
125      * component.
126      *
127      * If this is a proxy, returns the shared global component, else returns
128      * itself.
129      */

130     public AXIComponent getOriginal() {
131         if(getComponentType() == ComponentType.REFERENCE)
132             return this;
133
134         if(getComponentType() == ComponentType.PROXY)
135             return getSharedComponent().getOriginal();
136         
137         return this;
138     }
139     
140     /**
141      * Returns the shared component, if any, null otherwise.
142      */

143     public AXIComponent getSharedComponent() {
144         return sharedComponent;
145     }
146     
147     /**
148      * Returns true for a global component, false otherwise.
149      */

150     public boolean isGlobal() {
151         return (getParent() instanceof AXIDocument);
152     }
153     
154     /**
155      * Sets the shared component.
156      */

157     protected void setSharedComponent(AXIComponent sharedComponent) {
158         this.sharedComponent = sharedComponent;
159         if(sharedComponent == null) {
160             return;
161         }
162         AXIModelImpl thisModel = (AXIModelImpl)getModel();
163         if(thisModel == sharedComponent.getModel()) {
164             sharedComponent.addListener(this);
165             return;
166         }
167         
168         //keep listening to the other model
169
thisModel.listenToReferencedModel(sharedComponent.getModel());
170     }
171     
172     /**
173      * Add a listener to the shared component.
174      */

175     public void addListener(AXIComponent proxy) {
176         if(getModel() != proxy.getModel())
177             return;
178         
179         if(pcs == null)
180             pcs = new PropertyChangeSupport JavaDoc(this);
181         
182         PropertyChangeListener JavaDoc l = getWeakListener(proxy, false);
183         if(l != null)
184             pcs.addPropertyChangeListener(l);
185     }
186     
187     /**
188      * Remove listener from the shared component.
189      */

190     public void removeListener(AXIComponent proxy) {
191         if(pcs == null)
192             return;
193         
194         pcs.removePropertyChangeListener(getWeakListener(proxy, true));
195     }
196     
197     private void removeAllListeners() {
198         if(pcs == null)
199             return;
200         
201         for(AXIComponent listener: getRefSet()) {
202             removeListener(listener);
203         }
204     }
205     
206     /**
207      * Returns the list of components that are listening to this component.
208      */

209     public List JavaDoc<AXIComponent> getRefSet() {
210         if(pcs == null || listenerMap == null)
211             return null;
212         Set JavaDoc<AXIComponent> keySet = listenerMap.keySet();
213         return Collections.unmodifiableList(
214                 Arrays.asList(keySet.toArray(new AXIComponent[keySet.size()])));
215     }
216         
217     private PropertyChangeListener JavaDoc getWeakListener(AXIComponent proxy, boolean remove) {
218         if(listenerMap == null) {
219             listenerMap = new WeakHashMap JavaDoc<AXIComponent, PropertyChangeListener JavaDoc>();
220         }
221         if(remove)
222             return listenerMap.remove(proxy);
223         
224         if(proxy.getComponentType() != ComponentType.PROXY) {
225             Set JavaDoc<AXIComponent> keySet = listenerMap.keySet();
226             for(AXIComponent key : keySet) {
227                 if(key.getPeer() == proxy.getPeer())
228                     return null;
229             }
230         }
231         
232         PropertyChangeListener JavaDoc listener = listenerMap.get(proxy);
233         if(listener == null) {
234             listener = (PropertyChangeListener JavaDoc)WeakListeners.
235                     create(PropertyChangeListener JavaDoc.class, proxy, this);
236             listenerMap.put(proxy, listener);
237             return listener;
238         }
239         
240         //if exists, return null.
241
return null;
242     }
243     
244     /**
245      * Returns documentation for the schema component, if any.
246      */

247     public String JavaDoc getDocumentation() {
248         if(getPeer() == null ||
249            getPeer().getAnnotation() == null ||
250            getPeer().getAnnotation().getDocumentationElements() == null) {
251             return null;
252         }
253         
254         StringBuilder JavaDoc buffer = new StringBuilder JavaDoc();
255         for(Documentation doc : getPeer().getAnnotation().getDocumentationElements()) {
256             buffer.append(doc.getContent());
257         }
258         
259         return buffer.toString();
260     }
261     
262     /**
263      * Tells whether this component is mutable or not, w.r.t. the specified model.
264      * Returns true if this component does not belong to the given model,
265      * false otherwise.
266      */

267     public boolean isReadOnly() {
268         if(!isInModel())
269             return false;
270         
271         return (getModel().isReadOnly() || getModel() != getOriginal().getModel());
272     }
273     
274     /**
275      * Tells if this component supports cardinality or not.
276      * Returns false for all global elements and types, true otherwise.
277      */

278     public boolean supportsCardinality() {
279         return (getParent() instanceof AXIDocument) ? false: true;
280     }
281     
282     /**
283      * Returns true, if the children have been initialized, false otherwise.
284      */

285     public boolean canVisitChildren() {
286         return super.isChildrenInitialized();
287     }
288     
289     /**
290      * Returns true, for proxies and references, false otherwise.
291      */

292     public boolean isShared() {
293         ComponentType type = getComponentType();
294         if(type == ComponentType.PROXY || type == ComponentType.REFERENCE)
295             return true;
296         
297         return false;
298     }
299     
300     /**
301      * Returns the type of this component,
302      * may be local, shared, proxy or reference.
303      * @see ComponentType.
304      */

305     public ComponentType getComponentType() {
306         if(getParent() instanceof AXIDocument)
307             return ComponentType.SHARED;
308         
309         return ComponentType.LOCAL;
310     }
311     
312     /**
313      * Returns the content model, this AXI component belongs to.
314      * Returns null for a local component.
315      */

316     public ContentModel getContentModel() {
317         if(getComponentType() == ComponentType.PROXY) {
318             return getOriginal().getContentModel();
319         }
320                 
321         if(this instanceof ContentModel)
322             return (ContentModel)this;
323                     
324         AXIComponent parent = getParent();
325         if(parent == null ||
326            parent instanceof AXIDocument ||
327            parent instanceof Element)
328             return null;
329         
330         return parent.getContentModel();
331     }
332             
333     /**
334      * Returns the namespace, this component belongs to.
335      */

336     public String JavaDoc getTargetNamespace() {
337         if(getComponentType() == ComponentType.PROXY) {
338             return getOriginal().getTargetNamespace();
339         }
340             
341         SchemaComponent peer = getPeer();
342         return peer.getModel().getEffectiveNamespace(peer);
343     }
344         
345     /**
346      * Returns the strongly typed model,
347      * else the caller will have to cast.
348      */

349     public AXIModel getModel() {
350         return (AXIModel)super.getModel();
351     }
352     
353     /**
354      * Returns the parent {@link Element} for this component, if there is one,
355      * else null. If the component's immediate parent is an {@link Element},
356      * returns the parent, else, goes up in the hierarchy until it finds one.
357      * Returns null if none found.
358      */

359     public Element getParentElement() {
360         AXIComponent parent = (AXIComponent)getParent();
361         if(parent == null)
362             return null;
363         
364         if(parent instanceof Element)
365             return (Element)parent;
366         
367         return parent.getParentElement();
368     }
369     
370     /**
371      * Returns all the child {@link AbstractElement}s for this component.
372      */

373     public List JavaDoc<AbstractElement> getChildElements() {
374         List JavaDoc<AbstractElement> childrenElements = new ArrayList JavaDoc<AbstractElement>();
375         populateChildElements(childrenElements, this);
376         return Collections.unmodifiableList(childrenElements);
377     }
378     
379     private void populateChildElements(List JavaDoc<AbstractElement> childrenElements,
380             AXIComponent component) {
381         for(AXIComponent child : component.getChildren()) {
382             if( child instanceof ContentModel )
383                 continue;
384             
385             if( child instanceof AbstractElement ) {
386                 childrenElements.add((AbstractElement)child);
387                 continue;
388             }
389             populateChildElements(childrenElements, child);
390         }
391     }
392     
393     /**
394      * Returns the corresponding SchemaComponent.
395      * 0th should always be the absolute peer.
396      */

397     public final SchemaComponent getPeer() {
398         if(getComponentType() == ComponentType.REFERENCE) {
399             return peer;
400         }
401         
402         if(getSharedComponent() != null) {
403             return getSharedComponent().getPeer();
404         }
405         return peer;
406     }
407     
408     /**
409      * Sets the peer and resets the schema component listener.
410      */

411     public final void setPeer(SchemaComponent peer) {
412         if(getComponentType() == ComponentType.REFERENCE) {
413             this.peer = peer;
414             return;
415         }
416         
417         if(getSharedComponent() != null) {
418             getSharedComponent().setPeer(peer);
419             return;
420         }
421         
422         this.peer = peer;
423     }
424         
425     ///////////////////////////////////////////////////////////////////
426
//////////Implements AbstractComponent's abstract methods//////////
427
///////////////////////////////////////////////////////////////////
428
protected void appendChildQuietly(AXIComponent newComponent, List JavaDoc<AXIComponent> children) {
429         if(getComponentType() == ComponentType.PROXY) {
430             getOriginal().appendChildQuietly(newComponent, children);
431             return;
432         }
433         
434         children.add(newComponent);
435     }
436     
437     protected void insertAtIndexQuietly(AXIComponent newComponent, List JavaDoc<AXIComponent> children, int index) {
438         if(getComponentType() == ComponentType.PROXY) {
439             getOriginal().insertAtIndexQuietly(newComponent, children, index);
440             return;
441         }
442         children.add(index, newComponent);
443     }
444     
445     protected void removeChildQuietly(AXIComponent component, List JavaDoc<AXIComponent> children) {
446         if(getComponentType() == ComponentType.PROXY) {
447             getOriginal().removeChildQuietly(component, children);
448             return;
449         }
450         children.remove(component);
451     }
452                 
453     /**
454      * Visits each child schema component and creates
455      * corresponding axi component, adds them to the parent
456      * axi component.
457      */

458     public void populateChildren(List JavaDoc<AXIComponent> children) {
459         if(getSharedComponent() != null) {
460             Util.addProxyChildren(this, getSharedComponent(), children);
461             return;
462         }
463         
464         //Safeguard: this can be removed if DesignPatternTest goes through
465
if(getPeer() == null)
466             return;
467         
468         AXIModelBuilder builder = new AXIModelBuilder(this);
469         builder.populateChildren(getPeer(), true, children);
470     }
471         
472     protected Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
473         return super.clone();
474     }
475     
476     public AXIComponent copy(AXIComponent parent) {
477         AXIComponentFactory f = parent.getModel().getComponentFactory();
478         return f.copy(this);
479     }
480     
481     /**
482      * Checks if a component is part of an AXI model.
483      * Returns true if it has a valid parent and model, false otherwise.
484      */

485     protected boolean isInModel() {
486         //for AXIDocument, check if the model is valid
487
if(this instanceof AXIDocument)
488             return getModel() != null;
489         
490     //for everything else, both parent and model should be valid
491
return ( (getParent() != null) && (getModel() != null) );
492     }
493     
494     /**
495      * Must be called from all set methods.
496      */

497     protected void firePropertyChangeEvent(String JavaDoc property, Object JavaDoc oldVal, Object JavaDoc newVal) {
498     if (!isInModel())
499             return;
500         
501         fireValueChanged();
502         if(property != null)
503             firePropertyChange(property, oldVal, newVal);
504         if(pcs != null)
505             pcs.firePropertyChange(property, oldVal, newVal);
506     }
507     
508     /**
509      * Overwritten so that it can fire events to the proxies.
510      */

511     protected void appendChild(String JavaDoc property, AXIComponent child) {
512         if(getModel() != child.getModel())
513             return;
514         
515         super.appendChild(property, child);
516         if(pcs != null)
517             pcs.firePropertyChange(PROP_CHILD_ADDED, null, child);
518         
519         if(this instanceof AXIDocumentImpl)
520             ((AXIDocumentImpl)this).addToCache(child);
521     }
522     
523     /**
524      * Overwritten so that it can fire events to the proxies.
525      */

526     public void insertAtIndex(String JavaDoc property, AXIComponent child, int index) {
527         if(getModel() != child.getModel())
528             return;
529         
530         super.insertAtIndex(property, child, index);
531         if(pcs != null)
532             pcs.firePropertyChange(PROP_CHILD_ADDED, null, child);
533         
534         if(this instanceof AXIDocumentImpl)
535             ((AXIDocumentImpl)this).addToCache(child);
536     }
537     
538     /**
539      * Overwritten so that it can fire events to the proxies.
540      */

541     public void removeChild(String JavaDoc property, AXIComponent child) {
542         if(getModel() != child.getModel())
543             return;
544         
545         super.removeChild(property, child);
546         if(pcs != null) {
547             //fire event so that proxy children get deleted from their parents
548
pcs.firePropertyChange(PROP_CHILD_REMOVED, child, null);
549             //finally, remove all listeners from the shared child
550
child.removeAllListeners();
551         }
552         if(this instanceof AXIDocumentImpl)
553             ((AXIDocumentImpl)this).removeFromCache(child);
554     }
555     
556     /**
557      * Convenient method to append a child. If the parent is a proxy,
558      * delegates to the shared component.
559      */

560     public final void appendChild(AXIComponent child) {
561         if(getComponentType() == ComponentType.PROXY && !getModel().inSync()) {
562             getOriginal().appendChild(child);
563             return;
564         }
565         
566         appendChild(Util.getProperty(child), child);
567     }
568     
569     /**
570      * Convenient method to insert a child at a specified index.
571      * If the parent is a proxy, delegates to the shared component.
572      */

573     public final void addChildAtIndex(AXIComponent child, int index) {
574         if(getComponentType() == ComponentType.PROXY && !getModel().inSync()) {
575             getOriginal().addChildAtIndex(child, index);
576             return;
577         }
578         
579         insertAtIndex(Util.getProperty(child), child, index);
580     }
581         
582     /**
583      * Convenient method to remove a child.
584      * If the parent is a proxy, delegates to the shared component.
585      */

586     public final void removeChild(AXIComponent child) {
587         if(child.getComponentType() == ComponentType.REFERENCE) {
588             removeChild(Util.getProperty(child), child);
589             return;
590         }
591         
592         //proxy child delete from UI: delete original child
593
if(child.getComponentType() == ComponentType.PROXY &&
594            !getModel().inSync()) {
595             AXIComponent oChild = child.getOriginal();
596             oChild.getParent().removeChild(oChild);
597             return;
598         }
599         
600         removeChild(Util.getProperty(child), child);
601     }
602     
603     /**
604      * Removes all children one by one. This is a special case where
605      * removal is not delegated to the shared parent.
606      */

607     public void removeAllChildren() {
608         List JavaDoc<AXIComponent> removedChildren = new ArrayList JavaDoc<AXIComponent>();
609         for(AXIComponent child : getChildren()) {
610                 removedChildren.add(child);
611         }
612         for(AXIComponent child : removedChildren) {
613             removeChild(Util.getProperty(child), child);
614         }
615     }
616     
617     /////////////////////////////////////////////////////////////////////////////
618
////////// Following methods are applicable for proxies only ////////////////
619
/////////////////////////////////////////////////////////////////////////////
620
/**
621      * The proxy component receives an event notification.
622      */

623     public void propertyChange(PropertyChangeEvent JavaDoc evt) {
624         AXIComponent source = (AXIComponent)evt.getSource();
625         String JavaDoc property = evt.getPropertyName();
626         if(!isInModel()) {
627             //Ideally it shouldn't come here. Remove this as listener
628
//and make shared as null, so that it'll be GCed.
629
source.removeListener(this);
630             //setSharedComponent(null);
631
return;
632         }
633         //if(evt.getOldValue() == null && evt.getNewValue() != null) {
634
if(PROP_CHILD_ADDED.equals(property)) {
635             onChildAdded(evt);
636             return;
637         }
638         //if(evt.getOldValue() != null && evt.getNewValue() == null) {
639
if(PROP_CHILD_REMOVED.equals(property)) {
640             onChildDeleted(evt);
641             return;
642         }
643                 
644         firePropertyChangeEvent(evt.getPropertyName(),
645                 evt.getOldValue(), evt.getNewValue());
646     }
647
648     private void onChildAdded(PropertyChangeEvent JavaDoc evt) {
649         if(!isChildrenInitialized())
650             return;
651         AXIComponent parent = (AXIComponent)evt.getSource();
652         AXIComponent child = (AXIComponent)evt.getNewValue();
653         int index = -1;
654         for(int i=0; i<parent.getChildren().size(); i++) {
655             if(parent.getChildren().get(i) == child) {
656                 index = i;
657                 break;
658             }
659         }
660         if(index == -1)
661             return;
662         
663         AXIComponentFactory factory = getModel().getComponentFactory();
664         AXIComponent proxy = factory.createProxy(child);
665         insertAtIndex(Util.getProperty(child), proxy, index);
666     }
667     
668     private void onChildDeleted(PropertyChangeEvent JavaDoc evt) {
669         AXIComponent parent = (AXIComponent)evt.getSource();
670         AXIComponent child = (AXIComponent)evt.getOldValue();
671         if(child instanceof ContentModel) {
672             onContentModelDeleted((ContentModel)child);
673             return;
674         }
675         AXIComponent deletedChild = null;
676         for(AXIComponent c : getChildren()) {
677             if(c.getSharedComponent() == child) {
678                 deletedChild = c;
679                 break;
680             }
681         }
682         if(deletedChild == null)
683             return;
684         
685         removeChild(Util.getProperty(deletedChild), deletedChild);
686     }
687     
688     /**
689      * Removing a content model is special. For example, if element shipTo and billTo
690      * are of type USAddress, and USAddress gets deleted, then delete the proxy children
691      * of shipTo and billTo and finally delete USAddress.
692      */

693     private void onContentModelDeleted(ContentModel contentModel) {
694         List JavaDoc<AXIComponent> removeList = new ArrayList JavaDoc<AXIComponent>();
695         for(AXIComponent child : getChildren()) {
696             if(child.getContentModel() == contentModel) {
697                 removeList.add(child);
698             }
699         }
700         for(AXIComponent child: removeList) {
701             child.getParent().removeChild(Util.getProperty(child), child);
702         }
703     }
704     
705     /////////////////////////////////////////////////////////////////////
706
////////////////////////// member variables /////////////////////////
707
/////////////////////////////////////////////////////////////////////
708
/**
709      * Peer schema component.
710      */

711     private SchemaComponent peer;
712
713     /**
714      * Reference to the shared object, if this is a proxy.
715      */

716     protected AXIComponent sharedComponent;
717     private PropertyChangeSupport JavaDoc pcs;
718     private WeakHashMap JavaDoc<AXIComponent, PropertyChangeListener JavaDoc> listenerMap;
719     
720     private static final String JavaDoc PROP_CHILD_ADDED = "child_added";
721     private static final String JavaDoc PROP_CHILD_REMOVED = "child_removed";
722 }
723
Popular Tags