KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > schema2beans > BaseBean


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.schema2beans;
21
22 import java.util.*;
23 import java.io.*;
24 import java.beans.*;
25
26 import org.w3c.dom.*;
27 import java.lang.reflect.Constructor JavaDoc;
28
29 /**
30  * This class is the base class for any generated bean. This class provides
31  * the property book-keeping and access as a set of dynamic creation/accessor
32  * property methods.
33  *
34  * BaseBean is the class that any generated schema2beans class extends. The
35  * generated code implements the specific accessors methods while the BaseBean
36  * class provides the generic ones. For example, a generated bean would have
37  *
38  * public String getDescription() {
39  * return (String)this.getValue(DESCRIPTION);
40  * }
41  *
42  * BaseBean handles any value as an Object, where the generated schema2beans
43  * class handles a specific class/scalar type depending on its corresponding
44  * DTD element (and optional mdd values. See user document for mdd info).
45  *
46  * The idea of BaseBean is to provide the unique schema2beans java bean on
47  * which all generated schema2beans class rely. This is the user entry point
48  * of the schema2beans runtime. This is where you find all the generic methods
49  * that apply to all schema2beans classes: merge, find, clone, get/set attributes,
50  * etc.
51  *
52  * If we had to draw a gereric overview of the schema2beans architecture, we would
53  * draw three parts: BaseBean, BeanProp and DOMBinding. This is where most
54  * of the schema2beans runtime is implemented.
55  *
56  * There is one BaseBean instance per schema2beans node (one non final element
57  * of the DTD has one generated class that extends BaseBean).
58  * Each BaseBean holds its properties as BeanProp objects. For each property,
59  * BaseBean has one instance of BeanProp. For example, if we have as a DTD:
60  *
61  * <!ELEMENT book (chapter+, summary?)
62  * <!ELEMENT summary (#PCDATA)>
63  * <!ELEMENT line (#PCDATA)>
64  * <!ELEMENT chapter (line*)>
65  *
66  * The we would have two classes that would extend BaseBean: Book and Chapter.
67  * The book BaseBean would handle two instance of BeanProp, one for the
68  * property chapter and one of summary.
69  * The BeanProp for summary would handle a single String property, and the
70  * BeanProp for chapter would handle an indexed property of Chapter classes.
71  * Also, the chapter BaseBean (one instance for each element of the indexed
72  * property), would have one instance of BeanProp, that would handle the
73  * String indexed property line.
74  *
75  * The internal graph representation of the XML document is handled using
76  * a DOM tree. DOMBinding is the class that takes care of linking the
77  * BeanProp instances to the DOM nodes.
78  *
79  */

80 public abstract class BaseBean implements Cloneable JavaDoc, Bean {
81     
82     /*
83      * Because schema2beans doesn't generate user information about the
84      * relationship between the properties, BaseBean provides some
85      * method to give the user this information.
86      */

87     public class IterateChoiceProperties implements java.util.Iterator JavaDoc {
88         private ArrayList groups;
89         private int index;
90         
91         public IterateChoiceProperties() {
92             this.groups = new ArrayList();
93             this.index = 0;
94         }
95         
96         void add(BeanProp prop) {
97             if (prop.group != null && !this.groups.contains(prop.group))
98                 this.groups.add(prop.group);
99         }
100         
101         public boolean hasNext() {
102             return (this.index < this.groups.size());
103         }
104         
105         public Object JavaDoc next() throws NoSuchElementException {
106             if (hasNext()) {
107                 BeanProp.GroupProp gp =
108                         (BeanProp.GroupProp)this.groups.get(this.index++);
109                 return (BaseProperty[])gp.list();
110             } else
111                 throw new NoSuchElementException();
112         }
113         
114         public void remove()
115         throws UnsupportedOperationException JavaDoc, IllegalStateException JavaDoc {
116             throw new UnsupportedOperationException JavaDoc();
117         }
118     }
119     
120     // The binding object that links us to the DOM node. This might be null
121
// if there is no DOM Node yet (brand new object not attached to a graph)
122
protected DOMBinding binding;
123     
124     // A unique instance for all the nodes of the schema2beans tree
125
protected GraphManager graphManager;
126     
127     //
128
// HashMaps of the properties - those are the same properties but are
129
// sorted by Bean name (propByName) and by dtd name (propByDtdName).
130
//
131
private Map propByName;
132     private Map propByOrder;
133     
134     //
135
// If we use this hashMap, we have better performances whenever we
136
// access the element using the dtd name. Since schema2beans generate classes
137
// using Bean names, we can assume that very few accesses are performed
138
// using the dtd name and we can then save memory commenting out this
139
// hashMap. If performance is an issue while accessing the element using
140
// dtd names, we should consider using this hashmap again.
141
//
142
//private HashMap propByDtdName;
143

144     // When an attribute is added, we might not be part of a graph yet.
145
// In this case, we want to cache the attribute information and defer
146
// its processing. Whenever the node is added to a schema2beans tree,
147
// the values of the cache are automatically check to populate
148
// the attribute values.
149
private HashMap attrCache;
150     
151     // We can define an array of comparator, even though most of the usage
152
// is to have one comparator.
153
private ArrayList comparators;
154     
155     // This is a provision to mark at runtime the version of each node
156
// of the schema2beans tree. As this is not of much use for now, we simply
157
// save the memory space.
158
//private Version version;
159

160     // True if this node is the root of the schema2beans graph.
161
private boolean isRoot;
162     
163     // When the properties are created (calls from the generated class
164
// constructor), we give a number to each property so we know what
165
// their order is. This is how we know, when we add a new property
166
// where it should be added (it has to match the DTD order declaration)
167
private int propertyOrder;
168     
169     private String JavaDoc defaultNamespace;
170     
171     /*
172     public BaseBean() {
173         this(null, new Version(Version.MAJVER, Version.MINVER,
174                                Version.PTCVER));
175         System.out.println("warning: schema2beans.BaseBean: unknown version of generated beans being used.");
176     }
177      */

178     
179     /**
180      * @param comps the comparators to use. Can be null
181      * @param version which runtime version to be compatible with. This should be the version of the generated beans.
182      */

183     public BaseBean(Vector comps, Version version) {
184         init(comps, version);
185     }
186     
187     protected void init(Vector comps, Version version) {
188         if (version.getMajor() < 3) {
189             initPropertyTables(13);
190         }
191         this.comparators = new ArrayList(2);
192         //this.version = version;
193
this.isRoot = false;
194         this.propertyOrder = 0;
195         this.attrCache = null;
196         
197         if ((comps == null) || (comps.size()==0)) {
198             // Use the default comparator
199
this.comparators.add(new BeanComparator());
200         } else {
201             int size = comps.size();
202             for (int i=0; i<size; i++)
203                 this.comparators.add(comps.get(i));
204         }
205     }
206     
207     protected void initPropertyTables(int propertyCount) {
208         //
209
// In order to avoid a rehash, the initial capacity of a HashMap
210
// should be > the number of expected elements / load factor.
211
// If we make the load factor=1, then the initial capacity could be
212
// just the expected elements + 1. However, if 2 elements map to
213
// the same bucket, then they start getting strung together in
214
// a list, and that slows things down (you lose the O(1) performance).
215
// So, double the expected size to make it less likely that 2
216
// elements will map to the same bucket.
217
//
218
int hashTableSize = propertyCount * 2;
219         this.propByName = new HashMap(hashTableSize, 1.0f);
220         this.propByOrder = new HashMap(hashTableSize, 1.0f);
221         //this.propByDtdName = new HashMap(hashTableSize, 1.0f);
222
}
223     
224     //
225
// Dynamically add/remove a comparator. Comparator are used to compare
226
// and merge graphs. There are usually populated in the constructor
227
//
228
public synchronized void addBeanComparator(BeanComparator cmp) {
229         if (cmp != null)
230             this.comparators.add(cmp);
231     }
232     
233     public synchronized void removeBeanComparator(BeanComparator cmp) {
234         int i = this.comparators.indexOf(cmp);
235         if (i != -1)
236             this.comparators.remove(i);
237     }
238     
239     
240     /**
241      * Create a new property for the current bean, selecting default option
242      * values.
243      */

244     public void createProperty(String JavaDoc dtdName, String JavaDoc beanName, Class JavaDoc type) {
245         int o = Common.TYPE_0_1;
246         
247         if (type.isInstance(java.lang.String JavaDoc.class))
248             o |= Common.TYPE_STRING;
249         else
250             o |= Common.TYPE_BEAN;
251         
252         this.createProperty(dtdName, beanName, o, type);
253     }
254     
255     /**
256      * Create the root element of the graph
257      */

258     public void createRoot(String JavaDoc dtdName, String JavaDoc beanName,
259             int option, Class JavaDoc type) throws Schema2BeansRuntimeException {
260         BeanProp prop = new BeanProp(this, dtdName, beanName,
261                 option, type, true);
262         try {
263             this.graphManager.createRootBinding(this, prop, null);
264         } catch (Schema2BeansException e) {
265             throw new Schema2BeansRuntimeException(e);
266         }
267         this.isRoot = true;
268     }
269     
270     /**
271      * Create a new property for the current bean. This creates a BeanProp
272      * object (which is the internal representation of a single/indexed
273      * property) and adds it to the bean property hash table.
274      *
275      * This property is later accessed by its bean name.
276      */

277     public void createProperty(String JavaDoc dtdName, String JavaDoc beanName,
278             int option, Class JavaDoc type) throws
279             Schema2BeansRuntimeException {
280         
281         // This number represents the order of this property amoung
282
// its siblings. The beans generator generates the createProperty()
283
// method calls in the order of the DTD declaration. Therefore, we
284
// can simply relies on the order of the createProperty method calls
285
// to affect the order value for the property.
286
this.propertyOrder++;
287         
288         BeanProp prop = new BeanProp(this, dtdName, beanName, option, type);
289         prop.setOrder(this.propertyOrder);
290         Object JavaDoc obj1 = this.propByName.put(beanName, prop);
291         //Object obj2 = this.propByDtdName.put(dtdName, prop);
292
this.propByOrder.put(String.valueOf(this.propertyOrder), prop);
293         
294         if (obj1 != null) // || obj2 != null)
295
throw new Schema2BeansRuntimeException(Common.
296                     getMessage("DuplicateProperties_msg"));
297         
298         prop.initialize();
299     }
300     
301     public void setDefaultNamespace(String JavaDoc namespace) {
302         defaultNamespace = namespace;
303         createAttribute("xmlns", "xmlns", AttrProp.CDATA | AttrProp.IMPLIED,
304                 null, namespace);
305         setAttributeValue("xmlns", namespace);
306         if (beanProp().getAttrProp("xsi:schemaLocation", true) == null) {
307             createAttribute("xmlns:xsi", "xmlns:xsi", AttrProp.CDATA | AttrProp.IMPLIED, null, null);
308             createAttribute("xsi:schemaLocation", "xsi:schemaLocation", AttrProp.CDATA | AttrProp.IMPLIED, null, null);
309         }
310     }
311     
312     public String JavaDoc getDefaultNamespace() {
313         return defaultNamespace;
314     }
315     
316     /**
317      * Returns the list of properties of this bean as an array.
318      */

319     public BeanProp[] beanProps() {
320         int size = this.propByOrder.size();
321         BeanProp[] ret = new BeanProp[size];
322         for (int i=1; i<=size; i++)
323             ret[i-1] = (BeanProp)this.propByOrder.get(String.valueOf(i));
324         return ret;
325     }
326     
327     /**
328      * Returns the list of properties of this bean as an array.
329      */

330     protected Iterator beanPropsIterator() {
331         return propByName.values().iterator();
332     }
333     
334     /**
335      * Return the internal object representation of the property. This method
336      * cannot return null. If there is no object available for the specified
337      * property name, an exception is thrown.
338      */

339     public BeanProp beanProp(String JavaDoc name) {
340         BeanProp prop = (BeanProp)this.propByName.get(name);
341         
342         if (prop == null) {
343             // Search using the dtd name
344
String JavaDoc beanName = Common.convertName(name);
345             prop = (BeanProp)this.propByName.get(beanName);
346             
347             if (prop == null)
348                 throw new IllegalArgumentException JavaDoc(Common.
349                         getMessage("BeanPropertyDoesntExist_msg",
350                         this.getClass().getName(), name));
351         }
352         return prop;
353     }
354     
355     /**
356      * Return the internal object representation of the property. This method
357      * cannot return null. If there is no object available for the specified
358      * property name, an exception is thrown.
359      */

360     public BeanProp beanProp(int order) {
361         return (BeanProp)this.propByOrder.get(String.valueOf(order));
362     }
363     
364     /**
365      * Return the value of the single property named name.
366      */

367     public Object JavaDoc getValue(String JavaDoc name) {
368         return this.beanProp(name).getValue(0);
369     }
370     
371     /**
372      * Return one element of the indexed property named name.
373      */

374     public Object JavaDoc getValue(String JavaDoc name, int index) {
375         return this.beanProp(name).getValue(index);
376     }
377     
378     /**
379      * Return one element of the index property using the internal
380      * id index value. This index is unique and doesn't change over
381      * the time of the graph life. This method allows to get an element
382      * of an indexed property without keeping track of index shifting,
383      * and other elements removal.
384      */

385     public Object JavaDoc getValueById(String JavaDoc name, int id) {
386         return this.beanProp(name).getValueById(id);
387     }
388     
389     /**
390      * Convert an unique internal index value into the user visible
391      * index value. The property name specified must be an indexed
392      * property.
393      * This method may return -1 if we cannot figure out the index.
394      */

395     public int idToIndex(String JavaDoc name, int id) {
396         return this.beanProp(name).idToIndex(id);
397     }
398     
399     /**
400      * Convert the user index value into the internal unique index value.
401      * The property name specified must be an indexed property.
402      */

403     public int indexToId(String JavaDoc name, int index) {
404         return this.beanProp(name).indexToId(index);
405     }
406     
407     /**
408      * Return true if this property is null
409      */

410     public boolean isNull(String JavaDoc name) {
411         return (this.getValue(name) == null);
412     }
413     
414     /**
415      * Return true if this property is null
416      */

417     public boolean isNull(String JavaDoc name, int index) {
418         return (this.getValue(name, index) == null);
419     }
420     
421     /**
422      * Return the values of the indexed property named name. The result
423      * can be cast as an array of the property type.
424      */

425     public Object JavaDoc[] getValues(String JavaDoc name) {
426         return this.beanProp(name).getValues();
427     }
428     
429     /**
430      * Set the value for the single property named name.
431      */

432     public void setValue(String JavaDoc name, Object JavaDoc value) {
433         setValue(beanProp(name), 0, value);
434     }
435     
436     /**
437      * Set the value of an element for the indexed property named name.
438      */

439     public void setValue(String JavaDoc name, int index, Object JavaDoc value) {
440         setValue(beanProp(name), index, value);
441     }
442     
443     protected void setValue(BeanProp prop, int index, Object JavaDoc value) {
444         prop.setValue(index, value);
445     }
446     
447     protected int addValue(BeanProp prop, Object JavaDoc value) {
448         return prop.addValue(value);
449     }
450     
451     protected int removeValue(BeanProp prop, Object JavaDoc value) {
452         return prop.removeValue(value);
453     }
454     
455     protected void removeValue(BeanProp prop, int index) {
456         prop.removeValue(index);
457     }
458     
459     /**
460      * Set the value of an element for the indexed property named name,
461      * using the unique internal index.
462      */

463     public void setValueById(String JavaDoc name, int id, Object JavaDoc value) {
464         BeanProp bp = this.beanProp(name);
465         int index = bp.idToIndex(id);
466         bp.setValue(index, value);
467     }
468     
469     /**
470      * Set the values for the indexed property named name.
471      */

472     public void setValue(String JavaDoc name, Object JavaDoc[] value) {
473         this.beanProp(name).setValue(value);
474     }
475     
476     /**
477      * Add a value to the indexed property named name.
478      */

479     public int addValue(String JavaDoc name, Object JavaDoc value) {
480         return addValue(beanProp(name), value);
481     }
482     
483     /**
484      * Remove a value from the indexed property named name.
485      */

486     public int removeValue(String JavaDoc name, Object JavaDoc value) {
487         return removeValue(beanProp(name), value);
488     }
489     
490     /**
491      * Remove a value from the indexed property named name.
492      */

493     public void removeValue(String JavaDoc name, int index) {
494         removeValue(beanProp(name), index);
495     }
496     
497     /**
498      * Returns the position of the indexed property element.
499      * If the type of the property is a bean, use the == comparison,
500      * else use the equals() method.
501      * If the element is not found, return -1.
502      */

503     public int indexOf(String JavaDoc name, Object JavaDoc value) throws
504             Schema2BeansRuntimeException {
505         BeanProp bp = this.beanProp(name);
506         
507         if (bp == null)
508             throw new Schema2BeansRuntimeException(Common.
509                     getMessage("UnknownPropertyName_msg", name));
510         
511         if (Common.isArray(bp.type)) {
512             boolean isBean = Common.isBean(bp.type);
513             
514             int size = bp.size();
515             for (int i=0; i<size; i++) {
516                 Object JavaDoc obj = bp.getValue(i);
517                 if (isBean && (obj == value))
518                     return i;
519                 else
520                     if (!isBean && (obj.equals(value)))
521                         return i;
522             }
523         }
524         return -1;
525     }
526     
527     /**
528      * Return the size of the indexed property named name (the size
529      * might be greater than the number of elements if the indexed
530      * property array contains null elements).
531      */

532     public int size(String JavaDoc name) {
533         return this.beanProp(name).size();
534     }
535     
536     /**
537      * Return true if the property name is a choice property (defined
538      * in the DTD with the | char, such as (a | b | c ...)
539      */

540     public boolean isChoiceProperty(String JavaDoc name) {
541         return this.beanProp(name).isChoiceProperty();
542     }
543     
544     /**
545      * Equivalent to isChoiceProperty(name) on the current bean property.
546      */

547     public boolean isChoiceProperty() {
548         return this.beanProp().isChoiceProperty();
549     }
550     
551     /**
552      * If the property name is a choice property, returns the list of
553      * all the properties associated to this property (this one included).
554      * Return null otherwise.
555      */

556     public BaseProperty[] listChoiceProperties(String JavaDoc name) {
557         return this.beanProp(name).getChoiceProperties();
558     }
559     
560     /**
561      * Returns an iterator on the list of the sets of choice properties.
562      * Each object of the returned iterator is an array of BaseProperty.
563      * The returned value is never null be can be empty.
564      */

565     public Iterator listChoiceProperties() {
566         IterateChoiceProperties it = new IterateChoiceProperties();
567         Iterator i = beanPropsIterator();
568         while (i.hasNext())
569             it.add((BeanProp)i.next());
570         return it;
571     }
572     
573     /**
574      * Return the list of the properties
575      */

576     public BaseProperty[] listProperties() {
577         return (BaseProperty[])this.beanProps();
578     }
579     
580     /**
581      * Return the BaseProperty object for the current bean
582      */

583     public BaseProperty getProperty() {
584         return (BaseProperty)this.beanProp();
585     }
586     
587     /**
588      * Return the BaseProperty object for the specified property
589      */

590     public BaseProperty getProperty(String JavaDoc propName) {
591         return (BaseProperty)this.beanProp(propName);
592     }
593     
594     /**
595      * Return the known values as declared in the mdd file.
596      */

597     public Object JavaDoc[] knownValues(String JavaDoc name) {
598         return this.beanProp(name).knownValues();
599     }
600     
601     protected void addKnownValue(String JavaDoc name, Object JavaDoc value) {
602         this.beanProp(name).addKnownValue(value);
603     }
604     
605     /**
606      * Create a new attribute on the current bean.
607      */

608     public void createAttribute(String JavaDoc dtdName, String JavaDoc name, int type,
609             String JavaDoc[] values, String JavaDoc defValue) {
610         BeanProp bp = this.beanProp();
611         if (bp != null)
612             bp.createAttribute(dtdName, name, type, values, defValue);
613         else
614             System.out.println(Common.getMessage("beanPropIsNull_msg", name));
615     }
616     
617     /**
618      * Create a new attribute for the property propertyName. This creates
619      * the attribute within the BeanProp associated to the property.
620      */

621     public void createAttribute(String JavaDoc propName, String JavaDoc dtdName, String JavaDoc name,
622             int type, String JavaDoc[] values, String JavaDoc defValue) {
623         this.beanProp(propName).createAttribute(dtdName, name, type,
624                 values, defValue);
625     }
626     
627     /**
628      * Set the value of the attribute (see the BeanClass class)
629      */

630     public void setAttributeValue(String JavaDoc propName, String JavaDoc name,
631             String JavaDoc value) {
632         this.beanProp(propName).setAttributeValue(0, name, value);
633     }
634     
635     /**
636      * Set the attribute value on the current property bean
637      */

638     public void setAttributeValue(String JavaDoc name, String JavaDoc value) {
639         if (name == null)
640             return;
641         
642         BeanProp bp = this.beanProp();
643         if (bp != null) {
644             // Find out what our index is within the BeanProp object
645
int i = bp.idToIndex(this.binding.getId());
646             bp.setAttributeValue(i, name, value);
647         } else {
648             //
649
// There is no BeanProp/DOMBinding for this bean yet,
650
// cache the value.
651
//
652
if (this.attrCache == null)
653                 this.attrCache = new HashMap();
654             this.attrCache.put(name, value);
655         }
656     }
657     
658     /**
659      * Return the cached attribute values (when setAttributeValue is called
660      * before a newly created bean is part of a graph, the bean has no
661      * BeanProp/DOMBinding yet and the value has to be cached, waiting
662      * the element to be inserted into the graph. Only at this point
663      * the cached attribute values are ready to be really created).
664      */

665     String JavaDoc[] cachedAttributeNames() {
666         int size = (this.attrCache==null)?0:this.attrCache.size();
667         String JavaDoc[] ret = new String JavaDoc[size];
668         if (size >0) {
669             Iterator it = this.attrCache.keySet().iterator();
670             int i = 0;
671             while (it.hasNext())
672                 ret[i++] = it.next().toString();
673         }
674         
675         return ret;
676     }
677     
678     /**
679      * Return the value cached for the attribute named name.
680      */

681     String JavaDoc cachedAttributeValue(String JavaDoc name) {
682         if (this.attrCache != null)
683             return (String JavaDoc)this.attrCache.get(name);
684         else
685             return null;
686     }
687     
688     /**
689      * Return the value cached for the attribute named name.
690      */

691     void cachedAttributeClear() {
692         this.attrCache = null;
693     }
694     
695     /**
696      * Get the attribute value on the current property bean.
697      * If there is no current attribute (or element for that matter),
698      * then null is returned.
699      */

700     public String JavaDoc getAttributeValue(String JavaDoc name) {
701         BeanProp bp = this.beanProp();
702         if (bp != null) {
703             // Find out what our index is within the BeanProp object
704
int i = bp.idToIndex(this.binding.getId());
705             if (i < 0) // I guess we're not part of the BeanProp yet.
706
return null;
707             return bp.getAttributeValue(i, name);
708         } else {
709             //
710
// That's a brand new bean not attached yet to a graph. Try
711
// to get the value from the cache.
712
//
713
if (this.attrCache != null)
714                 return (String JavaDoc)this.attrCache.get(name);
715             else
716                 return null;
717         }
718     }
719     
720     /**
721      * Get the attribute value (see BeanProp class)
722      */

723     public String JavaDoc getAttributeValue(String JavaDoc propName, String JavaDoc name) {
724         return this.beanProp(propName).getAttributeValue(0, name);
725     }
726     
727     /**
728      * Set the value of the attribute (see the BeanClass class)
729      */

730     public void setAttributeValue(String JavaDoc propName, int index, String JavaDoc name,
731             String JavaDoc value) {
732         this.beanProp(propName).setAttributeValue(index, name, value);
733     }
734     
735     /**
736      * Get the attribute value (see BeanProp class)
737      */

738     public String JavaDoc getAttributeValue(String JavaDoc propName, int index, String JavaDoc name) {
739         return this.beanProp(propName).getAttributeValue(index, name);
740     }
741     
742     /**
743      * Return the list of all known attribute names for this property
744      * (even if they are not set).
745      */

746     public String JavaDoc[] getAttributeNames(String JavaDoc propName) {
747         return this.beanProp(propName).getAttributeNames();
748     }
749     
750     /**
751      * Return the list of all known attribute names for the current bean
752      */

753     public String JavaDoc[] getAttributeNames() {
754         BeanProp bp = this.beanProp();
755         if (bp != null)
756             return bp.getAttributeNames();
757         else
758             return null;
759     }
760     
761     
762     /**
763      * Return the list of all known attribute names for this property
764      * (even if they are not set).
765      */

766     public BaseAttribute[] listAttributes(String JavaDoc propName) {
767         return this.beanProp(propName).getAttributes();
768     }
769     
770     /**
771      * Return the list of all known attribute names for the current bean
772      */

773     public BaseAttribute[] listAttributes() {
774         BeanProp bp = this.beanProp();
775         if (bp != null)
776             return bp.getAttributes();
777         else
778             return null;
779     }
780     
781     
782     // Called by find() method. Where the attributes are searched for.
783
private void lookForAttribute(ArrayList found, BeanProp bp, BaseBean bean,
784             BaseAttribute[] attrs, String JavaDoc attrName,
785             Object JavaDoc value) {
786         
787         for (int j=0; j<attrs.length; j++) {
788             if (attrName == null || attrs[j].hasName(attrName)) {
789                 String JavaDoc name = attrs[j].getName();
790                 
791                 if (DDLogFlags.debug) {
792                     TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
793                             DDLogFlags.DBG_UBN, 1, DDLogFlags.FINDATTR,
794                             bp.getName() + "." + name +
795                             " for value " + value);
796                 }
797                 
798                 int size = bp.size();
799                 if (bp.isIndexed()) {
800                     for (int k=0; k<size; k++) {
801                         String JavaDoc v = bp.getAttributeValue(k, name);
802                         v = (String JavaDoc)Common.getComparableObject(v);
803                         
804                         if (DDLogFlags.debug) {
805                             TraceLogger.put(TraceLogger.DEBUG,
806                                     TraceLogger.SVC_DD,
807                                     DDLogFlags.DBG_UBN, 1,
808                                     DDLogFlags.FINDCMP,
809                                     bp.getName() + "." +
810                                     name + " = " + v);
811                         }
812                         
813                         if ((bean == null || bean == bp.getValue(k))
814                         && value.equals(v)) {
815                             if (DDLogFlags.debug) {
816                                 TraceLogger.put(TraceLogger.DEBUG,
817                                         TraceLogger.SVC_DD,
818                                         DDLogFlags.DBG_UBN, 1,
819                                         DDLogFlags.FNDATTR);
820                             }
821                             found.add(bp.buildFullName(k, name));
822                         }
823                     }
824                 } else {
825                     String JavaDoc v = bp.getAttributeValue(0, name);
826                     v = (String JavaDoc)Common.getComparableObject(v);
827                     
828                     if (DDLogFlags.debug) {
829                         TraceLogger.put(TraceLogger.DEBUG,
830                                 TraceLogger.SVC_DD,
831                                 DDLogFlags.DBG_UBN, 1,
832                                 DDLogFlags.FINDCMP,
833                                 bp.getName() + "." +
834                                 name + " = " + v);
835                     }
836                     
837                     if (value.equals(v)) {
838                         if (DDLogFlags.debug) {
839                             TraceLogger.put(TraceLogger.DEBUG,
840                                     TraceLogger.SVC_DD,
841                                     DDLogFlags.DBG_UBN, 1,
842                                     DDLogFlags.FNDATTR);
843                         }
844                         
845                         found.add(bp.buildFullName(0, name));
846                     }
847                 }
848             }
849         }
850     }
851     
852     /**
853      * Parse recursively the tree to find out a property value or attribute
854      * value.
855      *
856      * propName != null & attrName == null, find for property value
857      * propName == null & attrName != null, find for attribute value
858      * propName == null & attrName == null, find for any value
859      */

860     void find(BaseBean bean, ArrayList found, String JavaDoc propName,
861             String JavaDoc attrName, Object JavaDoc value) {
862         
863         if (DDLogFlags.debug) {
864             TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
865                     DDLogFlags.DBG_UBN, 1, DDLogFlags.FIND,
866                     (bean==null?"<null>":bean.getClass().getName()) +
867                     " - " + propName + "/" + attrName + " for value " +
868                     ((value == null)?"<null>":value.toString()));
869         }
870         
871         if (bean == null || value == null)
872             return;
873         
874         BaseProperty[] props = bean.listProperties();
875         
876         //
877
// Search our own attributes first (as any node might be the
878
// root of the search, we have to start by ourself first)
879
//
880
BaseAttribute[] attrs = bean.listAttributes();
881         if (propName == null && attrs != null && attrs.length > 0) {
882             BeanProp bp = bean.beanProp();
883             this.lookForAttribute(found, bp, bean, attrs, attrName, value);
884         }
885         
886         //
887
// Look for the properties and the attributes of the non-bean
888
// properties (bean property attributes are searched as the root
889
// case explained above)
890
//
891
for (int i=0; i<props.length; i++) {
892             BaseProperty p = props[i];
893             String JavaDoc name = p.getName();
894             BeanProp bp = (BeanProp)p;
895             int size = p.size();
896             
897             //
898
// Skip if this is a node (bean): we do not try to look for
899
// a node (we are searching a final value), and the attributes
900
// of a bean are searched at the beginning of the method.
901
//
902
if (!p.isBean()) {
903                 // Prop name & size
904
if (((propName != null && p.hasName(propName)) ||
905                         (propName == null && attrName == null))) {
906                     if (DDLogFlags.debug) {
907                         TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
908                                 DDLogFlags.DBG_UBN, 1,
909                                 DDLogFlags.FINDPROP,
910                                 name + " for value " + value);
911                     }
912                     
913                     if (p.isIndexed()) {
914                         // Search for a specific one
915
for (int j=0; j<size; j++) {
916                             Object JavaDoc v = bp.getValue(j);
917                             v = Common.getComparableObject(v);
918                             
919                             if (DDLogFlags.debug) {
920                                 TraceLogger.put(TraceLogger.DEBUG,
921                                         TraceLogger.SVC_DD,
922                                         DDLogFlags.DBG_UBN, 1,
923                                         DDLogFlags.FINDCMP,
924                                         name + "[" + j + "] = " +
925                                         v.toString());
926                                 
927                             }
928                             if (value.equals(v)) {
929                                 if (DDLogFlags.debug)
930                                     TraceLogger.put(TraceLogger.DEBUG,
931                                             TraceLogger.SVC_DD,
932                                             DDLogFlags.DBG_UBN, 1,
933                                             DDLogFlags.FNDPROP);
934                                 
935                                 found.add(bp.getFullName(j));
936                             }
937                         }
938                     } else {
939                         Object JavaDoc v = bp.getValue(0);
940                         v = Common.getComparableObject(v);
941                         
942                         if (DDLogFlags.debug) {
943                             TraceLogger.put(TraceLogger.DEBUG,
944                                     TraceLogger.SVC_DD,
945                                     DDLogFlags.DBG_UBN, 1,
946                                     DDLogFlags.FINDCMP,
947                                     name + " = " +
948                                     ((v==null)?"null":v.toString()));
949                         }
950                         
951                         if (value.equals(v)) {
952                             if (DDLogFlags.debug)
953                                 TraceLogger.put(TraceLogger.DEBUG,
954                                         TraceLogger.SVC_DD,
955                                         DDLogFlags.DBG_UBN, 1,
956                                         DDLogFlags.FNDPROP);
957                             
958                             found.add(bp.getFullName());
959                         }
960                     }
961                 }
962                 
963                 // Prop attributes
964
attrs = p.getAttributes();
965                 if (propName == null && attrs.length > 0)
966                     this.lookForAttribute(found, bp, null, attrs,
967                             attrName, value);
968             }
969             
970             // recurse
971
if (p.isBean() && p.isIndexed()) {
972                 BaseBean[] ba = (BaseBean[])bean.getValues(name);
973                 for (int k=0; k<ba.length; k++)
974                     find(ba[k], found, propName, attrName, value);
975             } else
976                 if (p.isBean()) {
977                 BaseBean b = (BaseBean)bean.getValue(name);
978                 find(b, found, propName, attrName, value);
979                 }
980         }
981     }
982     
983     /**
984      * Search for a specific attribute name/value.
985      */

986     public String JavaDoc[] findAttributeValue(String JavaDoc attrName, String JavaDoc value) {
987         ArrayList list = new ArrayList();
988         this.find(this, list, null, attrName, value);
989         String JavaDoc[] ret = new String JavaDoc[list.size()];
990         return (String JavaDoc[])list.toArray(ret);
991     }
992     
993     /**
994      * Search for a specfic property name/value.
995      */

996     public String JavaDoc[] findPropertyValue(String JavaDoc propName, Object JavaDoc value) {
997         ArrayList list = new ArrayList();
998         this.find(this, list, propName, null, value);
999         String JavaDoc[] ret = new String JavaDoc[list.size()];
1000        return (String JavaDoc[])list.toArray(ret);
1001    }
1002    
1003    /**
1004     * Search for any property or attribute of the specified value.
1005     */

1006    public String JavaDoc[] findValue(Object JavaDoc value) {
1007        ArrayList list = new ArrayList();
1008        this.find(this, list, null, null, value);
1009        String JavaDoc[] ret = new String JavaDoc[list.size()];
1010        return (String JavaDoc[])list.toArray(ret);
1011    }
1012    
1013    public void write(File f) throws IOException, Schema2BeansRuntimeException {
1014        OutputStream out = new FileOutputStream(f);
1015        try {
1016            write(out);
1017        } finally {
1018            out.close();
1019        }
1020    }
1021    
1022    /**
1023     * Write the current schema2beans graph as an XML document.
1024     */

1025    public void write(OutputStream out) throws IOException, Schema2BeansRuntimeException {
1026        try {
1027            reindent();
1028            if (this.graphManager != null) {
1029                this.graphManager.write(out);
1030            } else
1031                throw new IllegalStateException JavaDoc(Common.
1032                        getMessage("CantWriteBeanNotInDOMTree_msg"));
1033        } catch (Schema2BeansException e) {
1034            throw new Schema2BeansRuntimeException(e);
1035        }
1036    }
1037    
1038    /**
1039     * Write the current schema2beans graph as an XML document. The
1040     * parameter @param encoding tells us to write to @param out using
1041     * that encoding.
1042     */

1043    public void write(OutputStream out, String JavaDoc encoding) throws IOException, Schema2BeansException {
1044        reindent();
1045        if (this.graphManager != null) {
1046            this.graphManager.write(out, encoding);
1047        } else
1048            throw new IllegalStateException JavaDoc(Common.
1049                    getMessage("CantWriteBeanNotInDOMTree_msg"));
1050    }
1051    
1052    /**
1053     * If you call this method, you're responsible for setting up the
1054     * right encoding for the Writer @param w.
1055     */

1056    public void write(java.io.Writer JavaDoc w) throws IOException, Schema2BeansException {
1057        write(w, null);
1058    }
1059    
1060    /**
1061     * If you call this method, you're responsible for setting up the
1062     * right encoding for the Writer @param w. The @param encoding
1063     * parameter is there to let us know which encoding you picked so
1064     * that it can be written to the header.
1065     */

1066    public void write(java.io.Writer JavaDoc w, String JavaDoc encoding) throws IOException, Schema2BeansException {
1067        reindent();
1068        if (this.graphManager != null) {
1069            this.graphManager.write(w, encoding);
1070        } else
1071            throw new IllegalStateException JavaDoc(Common.
1072                    getMessage("CantWriteBeanNotInDOMTree_msg"));
1073    }
1074    
1075    public void writeNoReindent(OutputStream out) throws IOException, Schema2BeansException {
1076        if (this.graphManager != null) {
1077            this.graphManager.write(out);
1078        } else
1079            throw new IllegalStateException JavaDoc(Common.
1080                    getMessage("CantWriteBeanNotInDOMTree_msg"));
1081    }
1082    
1083    public void writeNode(java.io.Writer JavaDoc out) throws IOException {
1084        if (graphManager == null) {
1085            throw new IllegalStateException JavaDoc(Common.
1086                    getMessage("CantWriteBeanNotInDOMTree_msg"));
1087        }
1088        Node myNode = binding.getNode();
1089        try {
1090            graphManager.write(out, myNode);
1091        } catch (Schema2BeansException e) {
1092            // This exception does not make sense for our signature.
1093
throw new RuntimeException JavaDoc(e);
1094        }
1095    }
1096    
1097    /**
1098     * Rearrange the internal whitespace so that when it gets printed
1099     * out, it looks pretty.
1100     */

1101    public void reindent() {
1102        reindent(" ");
1103    }
1104    
1105    public void reindent(String JavaDoc indent) {
1106        if (graphManager != null) {
1107            graphManager.reindent(indent);
1108        } else
1109            throw new IllegalStateException JavaDoc(Common.
1110                    getMessage("CantWriteBeanNotInDOMTree_msg"));
1111    }
1112    
1113    protected boolean hasDomNode() {
1114        if (this.binding == null)
1115            return false;
1116        else
1117            return this.binding.hasDomNode();
1118    }
1119    
1120    protected DOMBinding domBinding() {
1121        return this.binding;
1122    }
1123    
1124    protected void setDomBinding(DOMBinding binding) {
1125        this.binding = binding;
1126    }
1127    
1128    /**
1129     * Return the unique graphManager instance of the schema2beans graph
1130     */

1131    public GraphManager graphManager() {
1132        return this.graphManager;
1133    }
1134    
1135    protected void setGraphManager(GraphManager graphMgr) {
1136        this.graphManager = graphMgr;
1137        if (this.changeListeners != null) {
1138            // XXXX - move these listeners to the final place.
1139
}
1140    }
1141    
1142    /*
1143     * The methods syncNodes, buildPathName and notifyChange are recursivly
1144     * called by the BeanProp objects to parse the entire tree, either
1145     * from the current bean up to the root, or from the current bean
1146     * down to any sub-nodes.
1147     */

1148    
1149    /**
1150     * This method is called to update the DOM graph with the set
1151     * of newly created beans. When a new bean subgraph is created,
1152     * all the beans of this subgraph are created independently from the
1153     * DOM graph. When the root of the bean subgraph is finally attached
1154     * to the original bean graph, we can find out where it fits on the
1155     * DOM graph, and this method is called to force all the beans cached
1156     * values to be flushed into the DOM graph (see also BeanProp and
1157     * DOMBinding syncNodes() methods).
1158     */

1159    void syncNodes(BeanProp.Action a) {
1160        Iterator i = beanPropsIterator();
1161        
1162        while (i.hasNext()) {
1163            BeanProp prop = (BeanProp)i.next();
1164            if (prop != null)
1165                prop.syncNodes(a);
1166        }
1167    }
1168    
1169    /**
1170     * Build the current path up to the root node. See the BeanProp object
1171     * for more details.
1172     */

1173    protected void buildPathName(StringBuffer JavaDoc str) {
1174        if (this.binding != null) {
1175            BeanProp p = this.binding.getBeanProp(this);
1176            if (p != null)
1177                p.buildPathName(this.binding, str);
1178        }
1179    }
1180    
1181    /**
1182     * This method is called by one of the properties of the bean
1183     * to notify of a change. This is the way the property uses to
1184     * signal all its ancestors about a property that has changed.
1185     */

1186    void notifyInternal(BeanProp.InternalEvent ie) {
1187        if (this.changeListeners != null
1188                && ie.type == BeanProp.InternalEvent.CHANGED) {
1189            
1190            boolean addedGM = false;
1191            
1192            if (this.graphManager == null) {
1193                this.graphManager = new GraphManager(this);
1194                addedGM = true;
1195            }
1196            this.changeListeners.
1197                    firePropertyChange(ie.getPropertyChangeEvent());
1198            if (addedGM) {
1199                this.graphManager = null;
1200            }
1201        }
1202        if (this.binding != null) {
1203            BeanProp p = this.binding.getBeanProp(this);
1204            if (p != null)
1205                p.notifyInternal(ie, true);
1206        }
1207    }
1208    
1209    
1210    /**
1211     * Process a deep clone() of the current bean
1212     */

1213    public Object JavaDoc clone() {
1214        BaseBean bean = null;
1215        
1216        try {
1217            // Create a new instance of ourself
1218
bean = (BaseBean)this.getClass().newInstance();
1219        } catch(Exception JavaDoc e) {
1220            TraceLogger.error(e);
1221            throw new Schema2BeansRuntimeException(Common.
1222                    getMessage("CantInstantiateBean_msg", e.getMessage()));
1223        }
1224        
1225        // If we are cloning the root - we need some extra initialization
1226
if (this.graphManager != null && this.graphManager.root == this) {
1227            // Get the info on the current root
1228
BeanProp p = this.binding.getBeanProp(this);
1229            String JavaDoc dtdName = p.getDtdName();
1230            String JavaDoc beanName = p.getDtdName();
1231            Class JavaDoc beanClass = p.getPropClass();
1232            
1233            // Create the initial DOM Node element
1234
Node n = GraphManager.createRootElementNode(dtdName);
1235            
1236            // Initialize the graph manager
1237
bean.graphManager.setXmlDocument(n);
1238            n = GraphManager.getElementNode(dtdName, n);
1239            bean.graphManager.completeRootBinding(bean, n);
1240        }
1241        
1242        // Copy the attributes of the root
1243
String JavaDoc[] attrs = this.getAttributeNames();
1244        if (attrs != null) {
1245            for(int j=0; j<attrs.length; j++) {
1246                String JavaDoc a = attrs[j];
1247                if (!this.beanProp().getAttrProp(a).isFixed()) {
1248                    String JavaDoc v = this.getAttributeValue(a);
1249                    if (bean.getAttributeValue(a) != v)
1250                        bean.setAttributeValue(a, v);
1251                }
1252            }
1253        }
1254        
1255        if (attrCache != null)
1256            bean.attrCache = (HashMap) attrCache.clone(); // This does a shallow clone of the HashMap, but that's fine since they're all just Strings in there.
1257

1258        Iterator it = beanPropsIterator();
1259        
1260        // Parse our attributes and copy them
1261
while (it.hasNext()) {
1262            BeanProp prop = (BeanProp)it.next();
1263            
1264            if (prop == null)
1265                continue;
1266            
1267            String JavaDoc name = prop.getBeanName();
1268            
1269            if (Common.isArray(prop.type)) {
1270                int size = prop.size();
1271                if (Common.isBean(prop.type)) {
1272                    for(int i=0; i<size; i++) {
1273                        BaseBean b = (BaseBean)prop.getValue(i);
1274                        if (b != null)
1275                            b = (BaseBean)b.clone();
1276                        bean.addValue(name, b);
1277                    }
1278                } else {
1279                    for(int i=0; i<size; i++)
1280                        bean.addValue(name, prop.getValue(i));
1281                    
1282                    // Copy the attributes
1283
attrs = prop.getAttributeNames();
1284                    for(int j=0; j<attrs.length; j++) {
1285                        String JavaDoc a = attrs[j];
1286                        if (!prop.getAttrProp(a).isFixed()) {
1287                            for(int i=0; i<size; i++) {
1288                                String JavaDoc v = prop.getAttributeValue(i, a);
1289                                if (bean.getAttributeValue(name, i, a) != v)
1290                                    bean.setAttributeValue(name, i, a, v);
1291                                
1292                            }
1293                        }
1294                    }
1295                }
1296            } else {
1297                if (Common.isBean(prop.type)) {
1298                    BaseBean b = (BaseBean)prop.getValue(0);
1299                    if (b != null)
1300                        b = (BaseBean)b.clone();
1301                    bean.setValue(name, b);
1302                } else {
1303                    bean.setValue(name, prop.getValue(0));
1304                    
1305                    // Copy the attributes
1306
attrs = prop.getAttributeNames();
1307                    for(int j=0; j<attrs.length; j++) {
1308                        String JavaDoc a = attrs[j];
1309                        if (!prop.getAttrProp(a).isFixed()) {
1310                            String JavaDoc v = prop.getAttributeValue(0, a);
1311                            if (bean.getAttributeValue(name, 0, a) != v)
1312                                bean.setAttributeValue(name, a, v);
1313                        }
1314                    }
1315                }
1316            }
1317        }
1318        
1319        return bean;
1320    }
1321    
1322    /**
1323     * Merge the bean tree with ourself
1324     *
1325     * Let's define:
1326     * G1 the current graph and G2 the new graph we want to merge
1327     * E1 the set of element of G1 that don't exist anymore in G2.
1328     * E2 the set of new elements of G2 that don't exist in G1.
1329     *
1330     * Then,
1331     * UPDATE is G1 - E1 + E2 (G1 becomes G2)
1332     * UNION is G1 U G2 <=> G1 + E2
1333     * INTERSECT is G1 n G2 <=> (G1 U G2) - E1 - E2
1334     */

1335    public static final int MERGE_NONE = 0x00;
1336    public static final int MERGE_INTERSECT = 0x01;
1337    public static final int MERGE_UNION = 0x02;
1338    public static final int MERGE_UPDATE = (MERGE_UNION|MERGE_INTERSECT);
1339    public static final int MERGE_COMPARE = 0x04;
1340    
1341    
1342    static String JavaDoc mergeModeToString(int mode) {
1343        switch(mode) {
1344            case MERGE_NONE: return "MERGE_NONE"; // NOI18N
1345
case MERGE_INTERSECT: return "MERGE_INTERSECT"; // NOI18N
1346
case MERGE_UNION: return "MERGE_UNION"; // NOI18N
1347
case MERGE_UPDATE: return "MERGE_UPDATE"; // NOI18N
1348
case MERGE_COMPARE: return "MERGE_COMPARE"; // NOI18N
1349
default: return "Unknown merge mode: " + mode; // NOI18N
1350
}
1351    }
1352    
1353    /**
1354     * Merge the specified bean schema2beans graph into the current graph using
1355     * the specified mode.
1356     */

1357    public void merge(BaseBean bean, int mode) {
1358        if (mode == MERGE_UPDATE)
1359            mergeUpdate(bean);
1360        else
1361            mergeTreeRoot(bean, mode);
1362    }
1363    
1364    /**
1365     * Merge the bean tree with the current graph using the default
1366     * merging option.
1367     */

1368    public void merge(BaseBean bean) {
1369        mergeUpdate(bean);
1370    }
1371    
1372    /**
1373     * Same as merge(BaseBean bean).
1374     * It's possible to override this method and make it more efficient,
1375     * than the generic one.
1376     */

1377    public void mergeUpdate(BaseBean sourceBean) {
1378        mergeTreeRoot(sourceBean, MERGE_UPDATE);
1379    }
1380    
1381    // Called by mergeTree to set the default hasKey value
1382
private boolean setHasKeyDefaultValue(BeanProp prop) {
1383        BeanComparator cmp = (BeanComparator)this.comparators.get(0);
1384        return cmp.hasKeyDefined(prop);
1385    }
1386    
1387    /*
1388     * Copy a property from the graph 'bean' to the graph 'prop', making
1389     * sure that both property and attributes are copied.
1390     * This method is used to copy non BaseBean properties
1391     * (a clone on a BaseBean automatically copies the attributes).
1392     */

1393    protected void copyProperty(BeanProp prop, BaseBean bean,
1394            int index, Object JavaDoc value) {
1395        
1396        boolean isArray = Common.isArray(prop.type);
1397        String JavaDoc name = prop.getName();
1398        
1399        // Copy the property value
1400
if (value == null) {
1401            if (isArray)
1402                value = bean.getValue(name, index);
1403            else
1404                value = bean.getValue(name, 0);
1405        }
1406        
1407        int newIndex = 0;
1408        
1409        if (isArray) {
1410            newIndex = addValue(prop, value);
1411        } else {
1412            setValue(prop, 0, value);
1413            index = 0;
1414        }
1415        
1416        this.copyAttributes(prop, newIndex, bean, index);
1417    }
1418    
1419    /*
1420     * This copies the attributes of a property from 'bean' (whole BaseBean)
1421     * to a property BeanProp (the specific BeanProp of the other BaseBean
1422     * where the copy as to occur).
1423     */

1424    private void copyAttributes(BeanProp prop, int propIndex,
1425            BaseBean bean, int beanIndex) {
1426        
1427        // Copy the attributes
1428
String JavaDoc name = prop.getName();
1429        BaseAttribute[] ba = bean.listAttributes(name);
1430        if (ba != null) {
1431            for(int j=0; j<ba.length; j++) {
1432                if (!ba[j].isFixed()) {
1433                    String JavaDoc attrName = ba[j].getName();
1434                    String JavaDoc v =
1435                            bean.getAttributeValue(name, beanIndex, attrName);
1436                    if (v != prop.getAttributeValue(propIndex, attrName)) {
1437                        prop.setAttributeValue(propIndex, attrName, v);
1438                    }
1439                }
1440            }
1441        }
1442    }
1443    
1444    /*
1445     * Entry point for the merge processing. This takes care of processing
1446     * the attribites of the root. This is necessary because the algorithm
1447     * assumes that we only merge two graphs that are identicals on their
1448     * first level (same simple property values and same attribute values).
1449     */

1450    synchronized boolean mergeTreeRoot(BaseBean bean, int mode) {
1451
1452        // Process the whole graph now
1453
return this.mergeTree(bean, mode);
1454    }
1455
1456    private boolean mergeAttributes(BaseBean bean, int mode) {
1457        boolean result = true;
1458        // We need to process the attributes of the root first
1459
BaseAttribute[] ba = bean.listAttributes();
1460
1461        // We might have no attribute on the root
1462
if (ba != null) {
1463            for (int j = 0; j < ba.length; j++) {
1464                BaseAttribute baseAttribute = ba[j];
1465                if (!baseAttribute.isFixed() && !baseAttribute.isTransient()) {
1466                    String JavaDoc attrName = baseAttribute.getName();
1467                    String JavaDoc curValue = this.getAttributeValue(attrName);
1468                    String JavaDoc otherValue = bean.getAttributeValue(attrName);
1469
1470                    if (curValue != otherValue) {
1471                        // Might have one of the two null, not both
1472
if (curValue == null || otherValue == null || !curValue.equals(otherValue)) {
1473                            if ((mode & MERGE_COMPARE) == MERGE_COMPARE) {
1474                                return false;
1475                            }
1476                            if ((mode & MERGE_UNION) == MERGE_UNION) {
1477                                this.setAttributeValue(attrName, otherValue);
1478                            }
1479                        }
1480                    }
1481                }
1482            }
1483        }
1484        return result;
1485    }
1486
1487    /**
1488     * Merge the bean tree with ourself
1489     */

1490    //
1491
// A little note about merge/comparison: if we compare the graphs
1492
// (option MERGE_COMPARE), we stop the comparison as soon as we find
1493
// two different elements, returning false. If we do not compare,
1494
// we always return true, until we parsed the whole graph.
1495
// Therefore, when recurse calls mergeTree we simply test the
1496
// result. If this is false, we can return immediatly (because this
1497
// is necessarily a COMPARE operation). If not, we have to continue
1498
// the merging/comparison.
1499
//
1500
synchronized boolean mergeTree(BaseBean bean, int mode) {
1501        if (DDLogFlags.debug) {
1502            TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
1503                    DDLogFlags.DBG_UBN, 1, DDLogFlags.MERGE,
1504                    this.getClass().getName() + "/" +
1505                    (bean==null?"<getAttributeValue(\"Id\")null>":bean.getClass().getName()) +
1506                    " - " + mergeModeToString(mode));
1507        }
1508
1509        //
1510
// The merge method is called only when two beans are logically
1511
// identical. Therefore, this method doesn't try to check if it
1512
// is equal to the other beans, but find out which properties
1513
// have to be updated.
1514
//
1515
if (this.getClass().isInstance(bean)) {
1516            if (!mergeAttributes(bean, mode)) {
1517                return false;
1518            }
1519
1520
1521            // We got the same as ourself in another graph
1522
Iterator it = beanPropsIterator();
1523
1524            //
1525
// Parse our attributes
1526
//
1527
// To have the following code easier to read, we could
1528
// call the setter/getter method of the BaseBean object for
1529
// both our attributes and the bean-to-merge attributes.
1530
// However, since we get the BeanProp objects from our
1531
// properties hashtable, we can call directly the BeanProp
1532
// getter/setter methods for our properties and the
1533
// BaseBean getter/setter for the bean we have to merge.
1534
//
1535
while (it.hasNext()) {
1536                // Get our next property (as a BeanProp)
1537
BeanProp prop = (BeanProp)it.next();
1538
1539                if (prop == null)
1540                    continue;
1541
1542                String JavaDoc name = prop.getBeanName();
1543                boolean isArray = Common.isArray(prop.type);
1544                boolean isBean = Common.isBean(prop.type);
1545                Object JavaDoc o1, o2, o3;
1546                boolean hasKey = false;
1547                boolean hasKeyDefined = false;
1548
1549                if (isArray) {
1550                    //
1551
// For each element of the index property, we have to
1552
// find if there is a matching element in the other
1553
// indexed property. If there is, merge the two
1554
// elements if this is a bean. If there are no
1555
// matching elements, remove it. At the end,
1556
// add any new elements of the other indexed property.
1557
//
1558
int i, j = 0;
1559                    int size1 = prop.size();
1560                    int size2 = bean.size(name);
1561                    boolean toRemove[] = new boolean[size1];
1562                    boolean toAdd[] = new boolean[size2];
1563                    boolean compared[] = new boolean[size2];
1564
1565                    // To keep track of that need to be removed
1566
Arrays.fill(toRemove, false);
1567
1568                    // To keep track of what we'll need to add after the loop
1569
Arrays.fill(toAdd, true);
1570
1571                    // To make sure that we do not match twice the same elt
1572
Arrays.fill(compared, false);
1573
1574                    if (DDLogFlags.debug) {
1575                        TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
1576                                DDLogFlags.DBG_UBN, 5,
1577                                DDLogFlags.MERGEPROP,
1578                                this.getClass().getName() + "." +
1579                                name + "[" + size1 + "] / " +
1580                                bean.getClass().getName() + "." +
1581                                name + "[" + size2 + "]");
1582                    }
1583
1584                    // For each of our current property elts ...
1585
for (i=0; i<size1; i++) {
1586                        o1 = prop.getValue(i);
1587                        //System.out.println("looking at prop "+i+" o1="+o1);
1588

1589                        if (isBean && o1 == null)
1590                            continue; // Nothing to compare
1591

1592                        boolean found = false;
1593
1594                        // ... try each comparator ...
1595
for (int c=0; c<this.comparators.size() && !found; c++){
1596                            BeanComparator cmp =
1597                                    (BeanComparator)this.comparators.get(c);
1598
1599                            // ... with every new property elts
1600
for (j=0; j<size2; j++) {
1601                                if (!compared[j]) {
1602                                    o2 = bean.getValue(name, j);
1603
1604                                    if (isBean) {
1605                                        if (o2 == null) {
1606                                            // Ignore null elt
1607
compared[j] = true;
1608                                            toAdd[j] = false;
1609                                            continue;
1610                                        }
1611
1612                                        o3 = cmp.compareBean(name,
1613                                                (BaseBean)o1,
1614                                                (BaseBean)o2);
1615
1616                                        if (!hasKey) {
1617                                            hasKey = cmp.hasKey();
1618                                            hasKeyDefined = true;
1619                                        }
1620
1621                                        if (o3 == o1) {
1622                                            // Beans identicals - recurse
1623
boolean ret = ((BaseBean)o1).
1624                                                    mergeTree((BaseBean)o2, mode);
1625
1626                                            if (!ret) return ret;
1627                                            compared[j] = true;
1628                                            found = true;
1629                                            break;
1630                                        }
1631                                    } else {
1632                                        o3 = cmp.compareProperty(name,
1633                                                this, o1, i,
1634                                                bean, o2, j);
1635                                        if (!hasKey) {
1636                                            hasKey = cmp.hasKey();
1637                                            hasKeyDefined = true;
1638                                        }
1639
1640                                        if (o3 == o1) {
1641                                            compared[j] = true;
1642                                            found = true;
1643                                            break;
1644                                        }
1645                                    }
1646                                }
1647                            }
1648                        }
1649
1650                        if (found) {
1651                            toAdd[j] = false; // already have it
1652

1653                            if (DDLogFlags.debug) {
1654                                TraceLogger.put(TraceLogger.DEBUG,
1655                                        TraceLogger.SVC_DD,
1656                                        DDLogFlags.DBG_UBN, 5,
1657                                        DDLogFlags.MERGEFOUND,
1658                                        name + "[" + i + "] <=> " +
1659                                        name + "[" + j + "]");
1660                            }
1661                        } else {
1662                            toRemove[i] = true; // no more exists
1663

1664                            if (DDLogFlags.debug) {
1665                                TraceLogger.put(TraceLogger.DEBUG,
1666                                        TraceLogger.SVC_DD,
1667                                        DDLogFlags.DBG_UBN, 5,
1668                                        DDLogFlags.MERGENTFND,
1669                                        name + "[" + i +
1670                                        "] to be removed");
1671                            }
1672                        }
1673                    }
1674
1675                    //
1676
// We want to make sure that we set a proper value
1677
// to hasKey when one of the two array is empty
1678
// (either null or containing null elements)
1679
//
1680
if (!hasKeyDefined)
1681                        hasKey = this.setHasKeyDefaultValue(prop);
1682
1683                    if ((mode & MERGE_COMPARE) == MERGE_COMPARE) {
1684                        // Any diff returns false
1685
for (i=0; i<size1; i++)
1686                            if (toRemove[i] && hasKey)
1687                                return false;
1688
1689                        for (j=0; j<size2; j++)
1690                            if (toAdd[j] && hasKey)
1691                                return false;
1692                    }
1693
1694                    // Remove, taking care of the index shifting
1695
if ((mode & MERGE_INTERSECT) == MERGE_INTERSECT) {
1696                        for (i=0, j=0; i<size1; i++) {
1697                            if (toRemove[i] && hasKey) {
1698                                //System.out.println("MERGE_INTERSECT: "+name+": removeValue("+prop.getBeanName()+", "+ (i-j)+")");
1699
removeValue(prop, i-j);
1700                                j++;
1701                            }
1702                        }
1703                    }
1704
1705                    // Add all the new elements
1706
if ((mode & MERGE_UNION) == MERGE_UNION) {
1707                        for (j=0; j < size2; j++) {
1708                            if (toAdd[j] && hasKey) {
1709                                //System.out.println("MERGE_UNION: "+name+": add j="+j);
1710
if (isBean) {
1711                                    // Attrs are within the BaseBean
1712
BaseBean srcBean = (BaseBean) bean.getValue(name, j);
1713                                    o2 = srcBean.clone();
1714                                    addValue(prop, o2);
1715                                    // Make sure that whitespace & comments get brought over.
1716
((BaseBean)o2).mergeTree(srcBean, mode);
1717                                } else {
1718                                    // We need to explicitely copy the attrs
1719
this.copyProperty(prop, bean, j, null);
1720                                }
1721                            }
1722                        }
1723                    }
1724                } else {
1725                    Object JavaDoc newValue = null;
1726                    boolean found = false;
1727
1728                    if (DDLogFlags.debug) {
1729                        TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
1730                                DDLogFlags.DBG_UBN, 5,
1731                                DDLogFlags.MERGEPROP,
1732                                this.getClass().getName() + "." +
1733                                name);
1734                    }
1735
1736                    // This is a single value property
1737
o1 = prop.getValue(0); // ourself
1738
o2 = bean.getValue(name); // the other one
1739

1740                    //
1741
// We have two properties to compare. Go over all the
1742
// comparators. Stop the comparison if any of them
1743
// find the two properties identical.
1744
// If they are beans, recurse calling the merge method
1745
// on these two beans. If they are not beans, simply
1746
// keep our current property value, ignoring the new one.
1747
//
1748
// If no comparator find the two properties identical,
1749
// update our current property with the value returned
1750
// by the very first comparator.
1751
//
1752
for (int c=0; c<this.comparators.size() && !found; c++) {
1753                        BeanComparator cmp =
1754                                (BeanComparator)this.comparators.get(c);
1755
1756                        if (isBean) {
1757                            if (o1 != null && o2 != null) {
1758                                // Recurse merging if they are the same
1759
o3 = cmp.compareBean(name, (BaseBean)o1,
1760                                        (BaseBean)o2);
1761
1762                                if (!hasKey)
1763                                    hasKey = cmp.hasKey();
1764
1765                                if (o3 != o1)
1766                                    newValue = (c==0)?o3:newValue;
1767                                else {
1768                                    found = true;
1769                                    boolean ret = ((BaseBean)o1).
1770                                            mergeTree((BaseBean)o2, mode);
1771                                    if (!ret) return ret;
1772                                }
1773                            } else {
1774                                if (o1 == o2)
1775                                    found = true;
1776                                else {
1777                                    hasKey = cmp.hasKeyDefined(prop);
1778                                    newValue = (c==0)?o2:newValue;
1779                                }
1780                            }
1781                        } else {
1782                            o3 = cmp.compareProperty(name, this, o1, -1,
1783                                    bean, o2, -1);
1784
1785                            if (!hasKey)
1786                                hasKey = cmp.hasKey();
1787
1788                            if (o3 != o1)
1789                                newValue = (c==0)?o3:newValue;
1790                            else
1791                                found = true;
1792                        }
1793
1794                        if (!found && ((mode & MERGE_COMPARE)==MERGE_COMPARE)) {
1795                            // Any diff - return false
1796
return false;
1797                        }
1798
1799                        if (!found && ((mode & MERGE_UNION) == MERGE_UNION)
1800                        && hasKey) {
1801
1802                            if (isBean) {
1803                                if (newValue != null) {
1804                                    setValue(prop, 0,
1805                                            ((BaseBean)newValue).clone());
1806                                } else {
1807                                    setValue(prop, 0, newValue);
1808                                }
1809                            } else {
1810                                // We need to explicitely copy the attrs
1811
this.copyProperty(prop, bean, 0, newValue);
1812                            }
1813
1814                            if (DDLogFlags.debug) {
1815                                TraceLogger.put(TraceLogger.DEBUG,
1816                                        TraceLogger.SVC_DD,
1817                                        DDLogFlags.DBG_UBN, 5,
1818                                        DDLogFlags.MERGENTFND,
1819                                        "updating with new value");
1820                            }
1821                        } else
1822                            if (found) {
1823                            if (DDLogFlags.debug) {
1824                                TraceLogger.put(TraceLogger.DEBUG,
1825                                        TraceLogger.SVC_DD,
1826                                        DDLogFlags.DBG_UBN, 5,
1827                                        DDLogFlags.MERGEFOUND,
1828                                        "keeping current value");
1829                            }
1830                            }
1831                    }
1832                }
1833            }
1834
1835
1836            if ((mode == MERGE_UPDATE)) {
1837                // For MERGE_UPDATE we additionally merge elements which have
1838
// no representation in model and exist only in DOMBinding
1839
if (isRoot) {
1840                    Schema2BeansUtil.mergeUnsupportedElements(this, bean);
1841                }
1842            }
1843
1844            //
1845
// For the MERGE_COMPARE option: if we reach this point, that
1846
// means we didn't find any diff. We can therefore return true.
1847
// Any other option returns always true.
1848
//
1849
return true;
1850        } else
1851            throw new IllegalArgumentException JavaDoc(Common.getMessage(
1852                    "MergeWrongClassType_msg", this.getClass().getName(),
1853                    (bean==null ? "<null>" : bean.getClass().getName())));
1854    }
1855
1856    /**
1857     * Compare 2 Node's and tell me if they're roughly equivalent.
1858     * By roughly equivalent, attributes and children are ignored.
1859     */

1860    private boolean areNodesEqual(Node node1, Node node2) {
1861        if (node1 == null && node2 == null)
1862            return true;
1863        if (node1 == null || node2 == null)
1864            return false;
1865        if (node1.getNodeType() != node2.getNodeType())
1866            return false;
1867        if (!node1.getNodeName().equals(node2.getNodeName()))
1868            return false;
1869        String JavaDoc value1 = node1.getNodeValue();
1870        String JavaDoc value2 = node2.getNodeValue();
1871        if (value1 == null) {
1872            if (value2 != null)
1873                return false;
1874        } else if (!value1.equals(value2))
1875            return false;
1876        return true;
1877    }
1878
1879    /**
1880     * Search in @param nodes for an equivalent node to @param node
1881     * (equivalent as defined by areNodesEqual) starting the search
1882     * at position @param start.
1883     */

1884    private int findInNodeList(NodeList nodes, Node node, int start) {
1885        return findInNodeList(nodes, node, start, nodes.getLength());
1886    }
1887    
1888    /**
1889     * Search in @param nodes for an equivalent node to @param node
1890     * (equivalent as defined by areNodesEqual) starting the search
1891     * at position @param start, giving up the search at position
1892     * @param maxPosition.
1893     */

1894    private int findInNodeList(NodeList nodes, Node node,
1895            int start, int maxPosition) {
1896        for (; start < maxPosition; ++start) {
1897            if (areNodesEqual(nodes.item(start), node))
1898                return start;
1899        }
1900        return -1;
1901    }
1902    
1903    /**
1904     * Perform a deep recursive comparison. Return true if the two graphs
1905     * are equals, false otherwise.
1906     *
1907     * The comparison is using the comparators and therfore returns a
1908     * result wrt to the comparators. That means a true return value
1909     * only means that the graphs are logically equals depending on the
1910     * comparator implementation.
1911     *
1912     * Note that the default comparator compares every property value.
1913     *
1914     */

1915    
1916    public boolean isEqualTo(Object JavaDoc obj) {
1917        boolean ret = false;
1918        
1919        try {
1920            if (this == obj)
1921                return true;
1922            else
1923                if (obj instanceof BaseBean)
1924                    ret = this.mergeTreeRoot((BaseBean)obj, MERGE_COMPARE);
1925        } catch(Exception JavaDoc e) {
1926            // Equals method only returns either true or false
1927
if (DDLogFlags.debug) {
1928                TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
1929                        DDLogFlags.DBG_UBN, 1, DDLogFlags.EQUALS,
1930                        "got exception while comparing: " +
1931                        e + "\n");
1932                e.printStackTrace();
1933            }
1934            ret = false;
1935        }
1936        
1937        if (DDLogFlags.debug) {
1938            TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
1939                    DDLogFlags.DBG_UBN, 1, DDLogFlags.EQUALS,
1940                    (ret?"true":"false"));
1941        }
1942        return ret;
1943    }
1944    
1945    /**
1946     * Get the bean using its unique identifier. This identifier is not
1947     * the indexed position of the element in the array but a unique id
1948     * associated to the DOMBinding object (see the BeanProp and DOMBinding
1949     * classes)
1950     */

1951    public Bean propertyById(String JavaDoc name, int id) {
1952        BeanProp bp = this.beanProp(name);
1953        
1954        if (Common.isBean(bp.type)) {
1955            if (Common.isArray(bp.type))
1956                return (BaseBean)bp.getValueById(id);
1957            else
1958                return (BaseBean)bp.getValue(0);
1959        } else
1960            throw new IllegalStateException JavaDoc(Common.
1961                    getMessage("PropertyIsNotABean_msg", name));
1962    }
1963    
1964    /**
1965     * Return the BeanProp object where this bean belongs.
1966     */

1967    public BeanProp beanProp() {
1968        if (this.binding != null)
1969            return this.binding.getBeanProp(this);
1970        else
1971            return null;
1972    }
1973    
1974    /**
1975     * Return the BaseBean parent of the current bean. This might return null
1976     * either because this is the root of the graph or because this node is not
1977     * part of a schema2beans graph yet.
1978     */

1979    public BaseBean parent() {
1980        BeanProp bp = this.beanProp();
1981        
1982        if (bp != null)
1983            return this.beanProp().getBean();
1984        else
1985            return null;
1986    }
1987    
1988    public Bean _getParent() {
1989        return parent();
1990    }
1991    
1992    /**
1993     * Return the root of the graph. If the graph is not connected to a
1994     * generated root, then the topmost bean is returned.
1995     */

1996    public Bean _getRoot() {
1997        Bean b = this;
1998        Bean bParent;
1999        while (!b.isRoot()) {
2000            bParent = b._getParent();
2001            if (bParent == null)
2002                break;
2003            b = bParent;
2004        }
2005        return b;
2006    }
2007    
2008    /**
2009     * Return the path name of this element in the graph.
2010     */

2011    public String JavaDoc fullName() {
2012        StringBuffer JavaDoc str = new StringBuffer JavaDoc();
2013        this.buildPathName(str);
2014        return str.toString();
2015    }
2016    
2017    public boolean hasName(String JavaDoc name) {
2018        if (name != null)
2019            return (name.equals(this.name()) || name.equals(this.dtdName()));
2020        else
2021            return false;
2022    }
2023    
2024    /**
2025     * Return true if this element is the root of the schema2beans tree.
2026     */

2027    public boolean isRoot() {
2028        return this.isRoot;
2029    }
2030    
2031    /**
2032     * Return the bean name of this schema2beans graph node.
2033     */

2034    public String JavaDoc name() {
2035        BeanProp bp = this.beanProp();
2036        
2037        if (bp != null)
2038            return this.beanProp().getBeanName();
2039        else
2040            return ""; // NOI18N
2041
}
2042    
2043    /**
2044     * Return the DTD name of this schema2beans graph node.
2045     */

2046    public String JavaDoc dtdName() {
2047        return this.beanProp().getDtdName();
2048    }
2049    
2050    public void createBean(Node node, GraphManager mgr) throws Schema2BeansRuntimeException {
2051        if (this.isRoot) {
2052            mgr.completeRootBinding(this, node);
2053        }
2054        
2055        this.graphManager = mgr;
2056        try {
2057            mgr.fillProperties(this.beanProps(), node);
2058        } catch (Schema2BeansException e) {
2059            throw new Schema2BeansRuntimeException(e);
2060        }
2061    }
2062    
2063    /**
2064     * Return a new instance of the specified bean property
2065     */

2066    public BaseBean newInstance(String JavaDoc name) {
2067        return this.beanProp(name).newBeanInstance();
2068    }
2069    
2070    
2071    public abstract void dump(StringBuffer JavaDoc str, String JavaDoc indent);
2072    
2073    // The old abstract verify method never did anything.
2074
// a validate method will get generated if the -validate option is on.
2075
//public abstract void validate() throws org.netbeans.modules.schema2beans.ValidateException;
2076

2077    /**
2078     * Dump the DOM content of this bean. If nodeName is specified,
2079     * dump only from the subname named nodeName.
2080     */

2081    public String JavaDoc dumpDomNode(String JavaDoc nodeName, int depth) {
2082        Node n = null;
2083        
2084        if (this.binding == null)
2085            return "<no binding>"; // NOI18N
2086
else {
2087            n = this.binding.node;
2088            if (n == null)
2089                return "<no node>"; // NOI18N
2090
}
2091        
2092        return DDFactory.XmlToString(n, depth, nodeName);
2093    }
2094    
2095    public String JavaDoc dumpDomNode(int depth) {
2096        return this.dumpDomNode(null, depth);
2097    }
2098    
2099    public String JavaDoc dumpDomNode() {
2100        return this.dumpDomNode(null, 99999);
2101    }
2102    
2103    public String JavaDoc dumpBeanNode() {
2104        return null;
2105    }
2106    
2107    public void dumpAttributes(String JavaDoc name, int index, StringBuffer JavaDoc str,
2108            String JavaDoc indent) {
2109        String JavaDoc[] names = this.getAttributeNames(name);
2110        
2111        for (int i=0; i<names.length; i++) {
2112            String JavaDoc v = this.getAttributeValue(name, index, names[i]);
2113            if (v != null) {
2114                str.append(indent + "\t attr: "); // NOI18N
2115
str.append(names[i]);
2116                str.append("="); // NOI18N
2117
str.append(v);
2118            }
2119        }
2120    }
2121    
2122    public String JavaDoc toString() {
2123        return this.name();
2124    }
2125    
2126    
2127    public void dumpXml() {
2128        try {
2129            write(System.out);
2130        } catch (IOException ee) {
2131            ee.printStackTrace();
2132        }
2133    }
2134    
2135    
2136    /**
2137     * Create the schema2beans graph of root bean clazz, from the input stream.
2138     */

2139    public static BaseBean createGraph(Class JavaDoc clazz, InputStream in)
2140    throws Schema2BeansException {
2141        return createGraph(clazz, in, false, null, null);
2142    }
2143    
2144    /**
2145     * Create the schema2beans graph of root bean clazz, from the input stream
2146     * in using the validate option.
2147     */

2148    public static BaseBean createGraph(Class JavaDoc clazz, InputStream in,
2149            boolean validate) throws Schema2BeansException {
2150        return createGraph(clazz, in, validate, null, null);
2151    }
2152    
2153    /**
2154     * Create the schema2beans graph of root bean clazz, from the input stream
2155     * in using the validate option and the entity resolver er.
2156     */

2157    public static BaseBean createGraph(Class JavaDoc clazz, InputStream in,
2158            boolean validate,
2159            org.xml.sax.EntityResolver JavaDoc er) throws
2160            Schema2BeansException {
2161        return createGraph(clazz, in, validate, er, null);
2162    }
2163    
2164    /**
2165     * Create the schema2beans graph of root bean clazz, from the input stream in
2166     * and using the validate, er and sh options.
2167     */

2168    public static BaseBean createGraph(Class JavaDoc clazz, InputStream in,
2169            boolean validate,
2170            org.xml.sax.EntityResolver JavaDoc er,
2171            org.xml.sax.ErrorHandler JavaDoc eh) throws
2172            Schema2BeansException {
2173        
2174        Constructor JavaDoc c = null;
2175        Document doc = null;
2176        BaseBean bean = null;
2177        
2178        doc = GraphManager.createXmlDocument(new org.xml.sax.InputSource JavaDoc(in),
2179                validate, er, eh);
2180        try {
2181            Class JavaDoc[] cc = new Class JavaDoc[] {org.w3c.dom.Node JavaDoc.class,
2182                    int.class};
2183                    c = clazz.getDeclaredConstructor(cc);
2184        } catch(NoSuchMethodException JavaDoc me) {
2185            throw new RuntimeException JavaDoc(Common.
2186                    getMessage("CantGetConstructor_msg"));
2187        }
2188        
2189        Object JavaDoc[] p = new Object JavaDoc[] {doc, new Integer JavaDoc(Common.NO_DEFAULT_VALUES)};
2190        
2191        try {
2192            bean = (BaseBean)c.newInstance(p);
2193        } catch (InstantiationException JavaDoc e) {
2194            throw new Schema2BeansNestedException(Common.getMessage(
2195                    "CantInstanciateBeanClass_msg"), e);
2196        } catch (IllegalAccessException JavaDoc e) {
2197            throw new Schema2BeansNestedException(Common.getMessage(
2198                    "CantInstanciateBeanClass_msg"), e);
2199        } catch (java.lang.reflect.InvocationTargetException JavaDoc e) {
2200            throw new Schema2BeansNestedException(Common.getMessage(
2201                    "CantInstanciateBeanClass_msg"), e);
2202        }
2203        
2204        return bean;
2205    }
2206    
2207    
2208    //
2209
PropertyChangeSupport changeListeners;
2210    
2211    
2212    //
2213
public void addPropertyChangeListener(PropertyChangeListener l) {
2214        BeanProp p = this.beanProp();
2215        if (p != null) {
2216            p.addPCListener(l);
2217        } else {
2218            if (this.changeListeners == null) {
2219                this.changeListeners = new PropertyChangeSupport(this);
2220            }
2221            this.changeListeners.addPropertyChangeListener(l);
2222        }
2223    }
2224    
2225    //
2226
public void removePropertyChangeListener(PropertyChangeListener l) {
2227        BeanProp p = this.beanProp();
2228        if (p != null) {
2229            p.removePCListener(l);
2230        } else if (this.changeListeners != null) {
2231            this.changeListeners.removePropertyChangeListener(l);
2232        }
2233    }
2234    
2235    //
2236
public void addPropertyChangeListener(String JavaDoc n, PropertyChangeListener l) {
2237        BeanProp p = this.beanProp(n);
2238        if (p != null)
2239            p.addPCListener(l);
2240    }
2241    
2242    //
2243
public void removePropertyChangeListener(String JavaDoc n, PropertyChangeListener l) {
2244        BeanProp p = this.beanProp(n);
2245        if (p != null)
2246            p.removePCListener(l);
2247    }
2248    
2249    /**
2250     * @return all Comment nodes found in this particular bean.
2251     * If there are no comments, then a 0 sized array will be
2252     * returned. null will never be returned.
2253     * If you want to change a comment, use Comment.setData().
2254     */

2255    public org.w3c.dom.Comment JavaDoc[] comments() {
2256        if (graphManager == null)
2257            return new org.w3c.dom.Comment JavaDoc[0];
2258        Document doc = graphManager.getXmlDocument();
2259        Node node = binding.getNode();
2260        NodeList children = node.getChildNodes();
2261        List foundComments = new LinkedList();
2262        for (int i = 0; i < children.getLength(); ++i) {
2263            Node child = children.item(i);
2264            if (child instanceof org.w3c.dom.Comment JavaDoc) {
2265                foundComments.add(child);
2266            }
2267        }
2268        org.w3c.dom.Comment JavaDoc[] result = new org.w3c.dom.Comment JavaDoc[foundComments.size()];
2269        result = (org.w3c.dom.Comment JavaDoc[]) foundComments.toArray(result);
2270        return result;
2271    }
2272    
2273    /**
2274     * The new comment will be added to the first of the bean's children.
2275     * @return the newly added Comment
2276     * @throws NullPointerException if this is a nonroot bean that is
2277     * not part of a rooted graph yet.
2278     */

2279    public org.w3c.dom.Comment JavaDoc addComment(String JavaDoc comment) {
2280        if (graphManager == null) {
2281            // ?
2282
}
2283        Document doc = graphManager.getXmlDocument();
2284        org.w3c.dom.Comment JavaDoc commentNode = doc.createComment(comment);
2285        Node node = binding.getNode();
2286        Node firstChild = node.getFirstChild();
2287        if (firstChild == null) {
2288            node.appendChild(commentNode);
2289        } else {
2290            node.insertBefore(commentNode, firstChild);
2291        }
2292        return commentNode;
2293    }
2294    
2295    /**
2296     * Remove @param comment from this bean.
2297     */

2298    public void removeComment(org.w3c.dom.Comment JavaDoc comment) {
2299        comment.getParentNode().removeChild(comment);
2300    }
2301    
2302    /**
2303     * @return child beans.
2304     */

2305    public BaseBean[] childBeans(boolean recursive) {
2306        List children = new LinkedList();
2307        childBeans(recursive, children);
2308        BaseBean[] result = new BaseBean[children.size()];
2309        return (BaseBean[]) children.toArray(result);
2310    }
2311    
2312    /**
2313     * Store all child beans into @param beans.
2314     */

2315    public void childBeans(boolean recursive, List beans) {
2316        BeanProp[] props = beanProps();
2317        BaseBean nextBean;
2318        for (int propPosition = 0; propPosition < props.length; ++propPosition) {
2319            BeanProp prop = props[propPosition];
2320            if (!prop.isBean()) {
2321                continue;
2322            }
2323            if (prop.isIndexed()) {
2324                for (int i = 0; i < prop.size(); ++i) {
2325                    nextBean = (BaseBean) prop.getValue(i);
2326                    if (nextBean == null)
2327                        continue;
2328                    beans.add(nextBean);
2329                    if (recursive)
2330                        nextBean.childBeans(true, beans);
2331                }
2332            } else {
2333                nextBean = (BaseBean) prop.getValue(0);
2334                if (nextBean == null)
2335                    continue;
2336                if (recursive)
2337                    nextBean.childBeans(true, beans);
2338                beans.add(nextBean);
2339            }
2340        }
2341    }
2342    
2343    public String JavaDoc nameSelf() {
2344        return beanProp().getFullName();
2345    }
2346    
2347    public String JavaDoc nameChild(Object JavaDoc childObj) {
2348        return nameChild(childObj, false, false);
2349    }
2350    
2351    public String JavaDoc nameChild(Object JavaDoc childObj, boolean returnConstName,
2352            boolean returnSchemaName) {
2353        return nameChild(childObj, returnConstName, returnSchemaName, false);
2354    }
2355    
2356    public String JavaDoc nameChild(Object JavaDoc childObj, boolean returnConstName,
2357            boolean returnSchemaName,
2358            boolean returnXPathName) {
2359        BeanProp[] props = beanProps();
2360        Object JavaDoc propValue;
2361        BeanProp prop = null;
2362        boolean found = false;
2363        int index = -2;
2364        propLoop:
2365            for (int propPosition = 0; propPosition < props.length; ++propPosition) {
2366                prop = props[propPosition];
2367                if (prop.isIndexed()) {
2368                    for (int i = 0; i < prop.size(); ++i) {
2369                        propValue = prop.getValue(i);
2370                        if (propValue == null ? childObj == null : propValue.equals(childObj)) {
2371                            found = true;
2372                            index = i;
2373                            break propLoop;
2374                        }
2375                    }
2376                } else {
2377                    propValue = prop.getValue(0);
2378                    if (propValue == null ? childObj == null : propValue.equals(childObj)) {
2379                        found = true;
2380                        break propLoop;
2381                    }
2382                }
2383            }
2384            if (found) {
2385                if (returnConstName)
2386                    return prop.getBeanName();
2387                else if (returnSchemaName)
2388                    return prop.dtdName;
2389                else if (returnXPathName) {
2390                    if (index < 0)
2391                        return prop.dtdName;
2392                    else
2393                        return prop.dtdName+"[position()="+index+"]";
2394                } else
2395                    return prop.getBeanName()+"."+Integer.toHexString(prop.indexToId(index));
2396            }
2397            return null;
2398    }
2399    
2400    public void changeDocType(String JavaDoc publicId, String JavaDoc systemId) {
2401        graphManager().setDoctype(publicId, systemId);
2402    }
2403    
2404    public void _setChanged(boolean changed) {
2405        throw new UnsupportedOperationException JavaDoc();
2406    }
2407    
2408    public String JavaDoc _getXPathExpr() {
2409        if (parent() == null) {
2410            return "/"+dtdName();
2411        } else {
2412            String JavaDoc parentXPathExpr = parent()._getXPathExpr();
2413            String JavaDoc myExpr = parent().nameChild(this, false, false, true);
2414            return parentXPathExpr + "/" + myExpr;
2415        }
2416    }
2417    
2418    public String JavaDoc _getXPathExpr(Object JavaDoc childObj) {
2419        String JavaDoc childName = nameChild(childObj, false, false, true);
2420        if (childName == null) {
2421            throw new IllegalArgumentException JavaDoc("childObj ("+childObj.toString()+") is not a child of this bean ("+dtdName()+")");
2422        }
2423        return _getXPathExpr() + "/" + childName;
2424    }
2425}
Popular Tags