KickJava   Java API By Example, From Geeks To Geeks.

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


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 // To dynamically build an array of the bean clastype (class[] getter method)
27
import java.lang.reflect.*;
28
29 import org.w3c.dom.*;
30
31
32 /**
33  * The BeanProp class is the internal representation of a schema2beans property.
34  * The three main classes that make the schema2beans architecture are: BaseBean,
35  * BeanProp and DOMBinding. BaseBean is the schema2beans java beans that maps
36  * a tag DTD element (BaseBean is the class extended by the generated code,
37  * specific to the DTD names). BaseBean contains as much as BeanProp object
38  * instances as it containes properties (element tags). A BeanProp handle
39  * both single and array properties. A single property is represented
40  * by one BeanProp instance that will be of type 'single' (TYPE_0_1 or TYPE_1).
41  * An array property is represented also by one BeanProp instance,
42  * but of type 'array' (TYPE_0_N or TYPE_1_N).
43  *
44  * The BeanProp class is handling properties, and is acting between
45  * the BaseBean (user view) and the DOMBinding (access to the DOM tree).
46  * The events, add/change/remove code and logic is on the BeanProp class.
47  */

48 public class BeanProp implements BaseProperty {
49     
50     
51     //
52
// The goal of the GroupProp class is to provide a common container
53
// for DTD or'd elements. Each or'd element of a same group references
54
// the same GroupProp instance.
55
// For example, (a | b | c) in a DTD generates 3 properties A, B, C. For
56
// each property, a BeanProp is created. One GroupProp is created
57
// for these 3 properties, and each BeanProp reference this GroupProp.
58
//
59
static class GroupProp {
60         ArrayList group;
61     
62         public GroupProp(BeanProp prop) {
63             this.group = new ArrayList();
64             this.add(prop);
65         }
66     
67         void add(BeanProp prop) {
68             this.group.add(prop);
69         }
70     
71         BeanProp[] list() {
72             int size = this.group.size();
73             BeanProp[] ret = new BeanProp[size];
74             for (int i=0; i<size; i++) {
75                 ret[i] = (BeanProp)this.group.get(i);
76             }
77             return ret;
78         }
79     }
80     
81     //
82
// Class used to identify the type of action to do in the recursive
83
// syncNodes() call.
84
//
85
static class Action {
86         public static final int ADD = 1;
87         public static final int REMOVE = 2;
88     
89         int action;
90         //java.beans.PropertyChangeEvent event;
91

92     
93         public Action(int action) {
94             this.action = action;
95             //this.event = null;
96
}
97     
98         public Action(int action, java.beans.PropertyChangeEvent JavaDoc event) {
99             this(action);
100             //this.event = event;
101
}
102     
103         public String JavaDoc toString() {
104             if (this.action == ADD)
105                 return "add"; // NOI18N
106
else
107                 return "remove";// NOI18N
108
}
109     }
110     
111     //
112
// We manage events only if someone adds a listener. This is a trivial
113
// optimization since we just turn on the events when they are used,
114
// not trying to keep track of how many listeners are registered.
115
//
116
static boolean handleEvents = false;
117     static boolean handleVetoEvents = false;
118     
119     
120     /*
121      * The Evt mgr takes care of queuing the events in order to trigger them
122      * only when all the changes are performed.
123      */

124     class EventMgr {
125         class Evt {
126             PropertyChangeEvent event;
127             boolean propagate;
128         
129             public Evt(PropertyChangeEvent evt, boolean p) {
130                 this.event = evt;
131                 this.propagate = p;
132             }
133         }
134     
135         private int delayed;
136         private BeanProp bp;
137         ArrayList events;
138     
139         public EventMgr(BeanProp bp) {
140             this.bp = bp;
141             this.delayed = 0;
142             this.events = new ArrayList(2);
143         }
144     
145         boolean isDelayed() {
146             return (this.delayed > 0);
147         }
148     
149         void delay() {
150             if (useEvents())
151                 this.delayed++;
152         }
153     
154         void addEvent(PropertyChangeEvent e, boolean propagate) {
155             if (useEvents()) {
156                 if (this.isDelayed()) {
157                     this.events.add(new Evt(e, propagate));
158                 } else {
159                     this.bp.notifyInternal(e, propagate);
160                 }
161             }
162         }
163     
164         void fireEvents() {
165             if (useEvents()) {
166                 if (this.delayed == 0)
167                     return;
168         
169                 this.delayed--;
170         
171                 if (this.delayed == 0) {
172                     int size = this.events.size();
173                     if (size > 0) {
174                         for (int i=0; i<size; i++) {
175                             Evt e = (Evt)this.events.get(i);
176                             this.bp.notifyInternal(e.event, e.propagate);
177                         }
178                         this.events.clear();
179                     }
180                 }
181             }
182         }
183     }
184     
185     static class InternalEvent {
186         static final int CHANGED = 1;
187         static final int VETOABLE = 2;
188     
189         int type;
190         Object JavaDoc obj;
191     
192         public InternalEvent(int type, Object JavaDoc obj) {
193             this.type = type;
194             this.obj = obj;
195         }
196     
197         PropertyChangeEvent getPropertyChangeEvent() {
198             return (PropertyChangeEvent)this.obj;
199         }
200     }
201     
202     //
203
// Used to define the setter operation of an indexed property.
204
// This is used when we call the local raiseVetoableEvent() method
205
// to know what the user plan to do in order to build the new value.
206
//
207
private static final int OP_SETTER_SETARRAY = 1;
208     private static final int OP_SETTER_SETELT = 2;
209     private static final int OP_SETTER_ADD = 4;
210     private static final int OP_SETTER_REMOVE = 8;
211     
212     // DTD name of the property
213
public String JavaDoc dtdName;
214     
215     // Java bean name of the property
216
public String JavaDoc beanName;
217     public int type;
218
219     // The java class that represent this property (for example, the schema2beans
220
// generated class)
221
public Class JavaDoc propClass;
222
223     // Array of DOMBinding, might contain only one element if TYPE_1
224
ArrayList bindings;
225
226     ArrayList knownValues;
227     
228     // List of attributes this property has
229
ArrayList attributes;
230     
231     // Bean this property belongs to
232
BaseBean bean;
233     
234     PropertyChangeSupport changeListeners;
235     VetoableChangeSupport vetoableListeners;
236     
237     EventMgr eventMgr;
238     
239     boolean isRoot;
240     
241     //
242
// This number represents the order of this property amoung all its
243
// sibling properties. This is used to create the DOM elements in
244
// the right place, with respect to the DTD declaration.
245
//
246
// For example, if we have <!ELEMENT a (b, c, d)>, then the order
247
// values for the properties b, c and d are: 1, 2 and 3.
248
// If we need to insert the property c, we know that is should
249
// be inserted before the property d, simply looking at its order
250
// number (see the BaseBean.createProperty for comment on the order
251
// value)
252
//
253
private int order;
254     
255     //
256
// If this property is defined as or'd (char |) in the DTD file,
257
// the following GroupProp instance holds references to the other
258
// BeanProp sharing the same or'd definition (a | b | c ...)
259
// The following group reference should be null if the property
260
// is simply defined as an 'and sequence' (a, b, c ...)
261
//
262
GroupProp group;
263     
264     
265     public BeanProp(BaseBean bean, String JavaDoc dtdName, String JavaDoc beanName,
266             int type, Class JavaDoc propClass) {
267         this(bean, dtdName, beanName, type, propClass, 10);
268     }
269     
270     public BeanProp(BaseBean bean, String JavaDoc dtdName, String JavaDoc beanName,
271             int type, Class JavaDoc propClass, int initialCapacity) {
272         //System.out.println("intitialCapacity="+initialCapacity);
273
this.dtdName = dtdName;
274     this.beanName = beanName;
275     this.type = type;
276     this.propClass = propClass;
277     this.bean = bean;
278     if (initialCapacity >= 0) {
279         this.bindings = new ArrayList(initialCapacity);
280         this.attributes = new ArrayList(initialCapacity);
281     }
282     this.changeListeners = null;
283     this.vetoableListeners = null;
284     this.knownValues = null;
285     this.isRoot = false;
286     this.order = 0;
287     this.eventMgr = new EventMgr(this);
288     this.group = null;
289     }
290     
291     public BeanProp(BaseBean bean, String JavaDoc dtdName, String JavaDoc beanName,
292             int type, Class JavaDoc propClass, boolean isRoot) {
293         this(bean, dtdName, beanName, type, propClass, isRoot, 10);
294     }
295
296     public BeanProp(BaseBean bean, String JavaDoc dtdName, String JavaDoc beanName,
297             int type, Class JavaDoc propClass, boolean isRoot, int initialCapacity) {
298     this(bean, dtdName, beanName, type, propClass, initialCapacity);
299     this.isRoot = isRoot;
300     }
301
302     /**
303      * Called by the BaseBean class when the property is fully created.
304      * (no more creation work needs to be done in the BaseBean for this
305      * property.). This is where any extra initialization work should take
306      * place if this couldn't be done in the constructor.
307      */

308     public void initialize() {
309     //
310
// If the property us or'd with others, group these properties
311
// together. The following assumes that the property are created
312
// through the BaseBean in the DTD order (which is how the GenBeans
313
// works), and also assumes that two sets of or'd properties never
314
// follow each others. (for example: (a | b) (c | d)).
315
//
316
if (Common.isSequenceOr(this.type)) {
317         // Try to get the previous reference, else create a new one
318
// (first element in the list of or'd elements)
319
BeanProp previousProp = null;
320         
321         if (this.order > 1) {
322         previousProp = this.bean.beanProp(this.order-1);
323         }
324         
325         if ((previousProp != null) && (previousProp.group != null)) {
326         // The previous property already have one - use it
327
previousProp.group.add(this);
328         this.group = previousProp.group;
329         }
330         
331         if (this.group == null) {
332         this.group = new GroupProp(this);
333         }
334     }
335     }
336     
337     boolean useEvents() {
338     return handleEvents;
339     }
340     
341     boolean useVetoEvents() {
342     return handleVetoEvents;
343     }
344     
345     void setOrder(int order) {
346     this.order = order;
347     }
348     
349     public Class JavaDoc getPropClass() {
350     return this.propClass;
351     }
352     
353     public int getType() {
354     return this.type;
355     }
356     
357     public String JavaDoc getBeanName() {
358     return this.beanName;
359     }
360     
361     public BaseBean getBean() {
362     return this.bean;
363     }
364     
365     public BaseBean getParent() {
366     return this.getBean();
367     }
368
369     NodeFactory getNodeFactory() {
370     return this.bean.graphManager().getNodeFactory();
371     }
372     
373     Node getParentNode() {
374     // If we are the root, there is no parent node
375
if (this.isRoot) {
376         return null;
377     }
378     
379     Node n = null;
380     DOMBinding b = this.bean.domBinding();
381     
382     if (b != null) {
383         n = b.getNode();
384     }
385     
386     if (n == null) {
387         throw new IllegalStateException JavaDoc(Common.getMessage("ParentNodeCantBeNull_msg"));
388     } else {
389         return n;
390     }
391     }
392     
393     //
394
// This method is called by the DOMBinding class when a DOM object
395
// needs to be inserted into the DOM graph. The goal of this method
396
// is to find out the next property (using the order value of the
397
// BeanProp) that has a DOM node, and if this is an indexed property,
398
// which DOM Node is the first of the list. This method makes sure that
399
// the new elements are inserted in the right property order (as specified
400
// by the DTD)
401
//
402
Node getFollowingSibling(DOMBinding binding) {
403     BeanProp bp = null;
404     int next = this.order+1;
405     
406     bp = this.bean.beanProp(next++);
407     
408     //System.out.println("getFollowingSibling next="+next+" bp="+bp);
409
while (bp != null) {
410         boolean found = false;
411         int size = bp.size();
412         
413         // Find out if there is any DOM Node
414
for (int i=0; i<size; i++) {
415         DOMBinding b = bp.getBinding(i);
416         if ((b != null) && (b.getNode() != null)) {
417             found = true;
418             break;
419         }
420         }
421         
422         if (found)
423         break;
424         
425         bp = this.bean.beanProp(next++);
426     }
427     
428     if (bp != null) {
429         if (Common.isArray(bp.type)) {
430             //
431
// We have a list of DOMBindings (then DOM nodes) that
432
// might be mixed (the first elt might not be the first node
433
// in the DOM graph). So, we have to find out in the list
434
// of element which one is the first one in the graph.
435
//
436
int size = bp.size();
437             for (int i=0; i<size; i++) {
438                 DOMBinding b1 = bp.getBinding(i);
439                 if (b1 != null) {
440                     boolean found = false;
441                     Node n1 = b1.getNode();
442                     Node p = n1.getPreviousSibling();
443                     // skip the whitespace between nodes
444
while (p instanceof Text)
445                         p = p.getPreviousSibling();
446             
447                     // If p is not amough the other, we have the first elt
448
for (int j=0; j<size; j++)
449                         if ( i != j) {
450                             DOMBinding b2 = bp.getBinding(j);
451
452                             if ((b2 != null) && (b2.getNode() == p)) {
453                                 // n is not the first one
454
found = true;
455                                 break;
456                             }
457                         }
458             
459                     // We got the first element of our indexed property
460
if (!found) {
461                         return n1;
462                     }
463                 }
464             }
465         }
466         else {
467             // Single property - only one node, return it
468
return bp.getBinding(0).getNode();
469         }
470     }
471     
472     return null;
473     }
474     
475     
476     DOMBinding getBinding(int index) {
477     return (DOMBinding)this.bindings.get(index);
478     }
479
480     protected int bindingsSize() {
481         return bindings.size();
482     }
483     
484     //////
485
//
486
// Add, Get, Remove values
487
//
488

489     public Object JavaDoc getValue(int index) {
490         if (!Common.isArray(this.type)) {
491             // Value not set for single type property - return null
492
if ((index > 0) || (this.bindingsSize() == 0))
493                 return null;
494         }
495         if (bindingsSize() == 0) {
496             // Ok no value set so far. return null.
497
return null;
498         } // end of if (this.bindingsSize() == 0)
499

500         DOMBinding b = (DOMBinding)this.bindings.get(index);
501
502         if (b != null)
503             return b.getValue(this);
504         else
505             return null;
506     }
507     
508     
509     /**
510      * Return the element which as the unique DOMBinding identifier id
511      * (Every DOMBinding has a unique identifier which identifies uniquely
512      * the DOM element referenced by the DOMBinding object. This id is,
513      * for example, used to build the absolute name of an elt of the graph)
514      */

515     public Object JavaDoc getValueById(int id) {
516         int size = this.bindingsSize();
517         for (int i=0; i<size; i++) {
518             DOMBinding b = (DOMBinding)this.bindings.get(i);
519         
520             if (b.id == id)
521                 return b.getValue(this);
522         }
523         return null;
524     }
525     
526     /**
527      * Convert the relative index value (the property index value) into
528      * the unique DOMBinding id.
529      */

530     public int indexToId(int index) {
531         if (index>=0 && index<this.bindingsSize()) {
532             DOMBinding b = (DOMBinding)this.bindings.get(index);
533             if (b != null)
534                 return b.id;
535         }
536         return -1;
537     }
538     
539     /**
540      * Convert a unique DOMBinding Id into a relative index value
541      * This method may return -1 if we cannot figure out the index.
542      */

543     public int idToIndex(int id) {
544         int size = this.bindingsSize();
545         for (int i=0; i<size; i++) {
546             DOMBinding b = (DOMBinding)this.bindings.get(i);
547             
548             if ((b != null) && (b.id == id))
549                 return i;
550         }
551         return -1;
552     }
553     
554     //
555
// Build an Object[] of the current indexed property value
556
// Make room for extra elements at the end of the array.
557
//
558
protected Object JavaDoc[] getObjectArray(int extraElements) {
559     int size = this.bindingsSize();
560     Object JavaDoc a =
561         Array.newInstance(this.propClass,
562             ((size+extraElements>=0)?(size+extraElements):size));
563     
564     // If we have enough space
565
int i=0;
566         try {
567             if (extraElements >= 0) {
568                 for (i=0; i<size; i++)
569                     Array.set(a, i, this.getValue(i));
570             }
571         } catch(IllegalArgumentException JavaDoc e) {
572             e.printStackTrace();
573             System.err.println("bean: " + this.getName() + "dtdname: " +
574                                this.getDtdName() +
575                                "class: " + this.getPropertyClass());
576             Object JavaDoc o = this.getValue(i);
577             if (o != null)
578                 System.err.println("elt: " + o.toString() + " - " +
579                                    o.getClass());
580             else
581                 System.err.println("elt is null for index " + i);
582
583             try {
584                 this.getBean().write(System.err);
585                 System.err.println(this.getBean().dumpDomNode());
586             } catch (java.io.IOException JavaDoc e2) {
587             }
588             throw e;
589         }
590     
591     return (Object JavaDoc[])a;
592     }
593     
594     
595     /**
596      * If the property is an indexed property, return all the values
597      * as an array of its type.
598      */

599     public Object JavaDoc[] getValues() {
600     if (!Common.isArray(this.type))
601         throw new IllegalStateException JavaDoc(Common.
602         getMessage("NotIndexedProperty_msg"));
603     
604     return this.getObjectArray(0);
605     }
606     
607     /**
608      * Set the whole index property. Element might have changed, been removed
609      * added or just mixed.
610      */

611     public void setValue(Object JavaDoc[] value) {
612         if (Common.isVetoable(this.type) && useVetoEvents()) {
613             this.raiseVetoableEvent(value, 0, OP_SETTER_SETARRAY);
614         
615             // If we reach this point, no exception has been raised,
616
// and the change can happen.
617
}
618     
619         //
620
// Remove deleted element, add the new ones then sort the new array.
621
//
622
DOMBinding b;
623         int newSize = 0;
624         int size = this.bindingsSize();
625         boolean found;
626         boolean skipNew[] = null;
627         int i, j;
628         boolean changed = false; // track of any change
629
Object JavaDoc oldValue = null;
630     
631         if (useEvents()) {
632             oldValue = Array.newInstance(this.propClass, size);
633         }
634     
635         // Do not send any event while updating the array
636
this.eventMgr.delay();
637     
638         if (value != null) {
639             newSize = value.length;
640             skipNew = new boolean[newSize];
641             // Remove all unused elements
642
Arrays.fill(skipNew, false);
643         }
644     
645         // Mark the original position of the DOM nodes
646
for (i=0; i<size; i++) {
647             DOMBinding d = (DOMBinding)this.bindings.get(i);
648             if (d != null)
649                 d.posDOM = i;
650         }
651     
652         for (i=0; i<size; i++) {
653             found = false;
654             Object JavaDoc o = this.getValue(i);
655             if (o == null)
656                 continue;
657         
658             // Do a complete loop search for same object ref
659
for (j=0; j<newSize; j++)
660                 if (!skipNew[j]) {
661                     if (value[j] == null)
662                         continue;
663                     if (o == value[j]) {
664                         found = true;
665                         break;
666                     }
667                 }
668         
669             // Go now for same object content
670
if (!found) {
671                 for (j=0; j<newSize; j++)
672                     if (!skipNew[j]) {
673                         if (value[j] == null)
674                             continue;
675                         if (o.equals(value[j])) {
676                             found = true;
677                             break;
678                         }
679                     }
680             }
681         
682             if (!found) {
683                 // No more in the new list - remove it
684
if (useEvents()) {
685                     if (Common.isBean(this.type))
686                         Array.set(oldValue, i, ((BaseBean)o).clone());
687                     else
688                         Array.set(oldValue, i, o);
689             
690                     changed = true;
691                 }
692                 this.removeElement(i, false);
693             }
694             else {
695                 // Also in the original list - skip this one
696
if (useEvents()) {
697                     Array.set(oldValue, i, o);
698                     if (i != j)
699                         changed = true;
700                 }
701                 skipNew[j] = true;
702                 b = (DOMBinding)this.bindings.get(i);
703                 // Will live at position j
704
b.pos = j;
705             }
706         }
707     
708         // Add the new ones
709
for (i=0; i<newSize; i++)
710             if (!skipNew[i]) {
711                 if (value[i] != null) {
712                     // Add value
713
int idx = this.setElement(0, value[i], true);
714                     b = (DOMBinding)this.bindings.get(idx);
715                     // Should live at position i
716
b.pos = i;
717                     // currently located at position idx in the DOM
718
b.posDOM = idx;
719                     changed = true;
720                 }
721             }
722     
723         // Sort the final array to match the parameter elements order
724
ArrayList newBindings = new ArrayList(newSize);
725         for (i=0; i<newSize; i++)
726             newBindings.add(null);
727         newBindings.ensureCapacity(newSize+1);
728         size = this.bindingsSize();
729         for (i=0; i<size; i++) {
730             b = (DOMBinding)this.bindings.get(i);
731             if (b != null) {
732                 newBindings.set(b.pos, b);
733             }
734         }
735         // This should match the parameter order
736
this.bindings = newBindings;
737     
738         if (changed && this.bean.binding != null) {
739             // Also trigger an event for the whole indexed property
740
PropertyChangeEvent e = this.createEvent(this.bean.binding,
741                                                      oldValue, value, null);
742             this.notifyInternal(e, true);
743         }
744     
745         //
746
// We have now to reorder the DOM nodes
747
//
748
if (changed) {
749             for (i=0; i<newSize; i++) {
750                 DOMBinding d1 = (DOMBinding)this.bindings.get(i);
751                 if (d1 == null)
752                     continue;
753         
754                 DOMBinding db = null;
755
756                 // Do we have anyone before us (more left than us)
757
int min = d1.posDOM;
758                 for (j=i+1; j<newSize; j++) {
759                     DOMBinding d2 = (DOMBinding)this.bindings.get(j);
760                     if (d2.posDOM < min) {
761                         min = d2.posDOM;
762                         db = d2;
763                     }
764                 }
765                 // If found one, insert before as re-order from the left
766
if (db != null)
767                     d1.moveBefore(this, db.getNode());
768             }
769         }
770     
771         // If anything changed, fire the events now
772
this.eventMgr.fireEvents();
773     }
774     
775     /**
776      * Set the value for one element of an indexed property
777      */

778     public void setValue(int index, Object JavaDoc value) {
779     if (Common.isVetoable(this.type) && useVetoEvents()) {
780         // Check before doing any change if this is ok
781
this.raiseVetoableEvent(value, index, OP_SETTER_SETELT);
782         
783         // If we reach this point, no exception has been raised,
784
// and the change can happen.
785
}
786     
787     this.setElement(index, value, false);
788     }
789     
790     /**
791      * Add a value at the end of the indexed property
792      */

793     public int addValue(Object JavaDoc value) {
794     if (Common.isVetoable(this.type) && useVetoEvents()) {
795         // Check before doing any change if this is ok
796
this.raiseVetoableEvent(value, 0, OP_SETTER_ADD);
797         
798         // If we reach this point, no exception has been raised,
799
// and the change can happen.
800
}
801     
802     return this.setElement(0, value, true);
803     }
804     
805     /**
806      * Remove the object that have the bean value (.equals call)
807      *
808      * removeValue do not leave null slots as the setValue does.
809      *
810      * Remove tries first to find out the same object reference
811      * within the list of properties values. If not, it tries
812      * to find out an object which is equal.
813      */

814     public int removeValue(Object JavaDoc value) {
815     if (Common.isVetoable(this.type) && useVetoEvents()) {
816         // Check before doing any change if this is ok
817
this.raiseVetoableEvent(value, 0, OP_SETTER_REMOVE);
818         
819         // If we reach this point, no exception has been raised,
820
// and the change can happen.
821
}
822     
823     
824     int size = this.bindingsSize();
825     int index = -1;
826     
827     for (int i=0; (i<size) && (index == -1); i++) {
828         DOMBinding b = (DOMBinding)this.bindings.get(i);
829         if (b != null) {
830         Object JavaDoc o = b.getValue(this);
831         if ((o!=null) && (o == value))
832             index = i;
833         }
834     }
835     
836     for (int i=0; (i<size) && (index == -1); i++) {
837         DOMBinding b = (DOMBinding)this.bindings.get(i);
838         if (b != null) {
839         Object JavaDoc o = b.getValue(this);
840         if ((o!=null) && (o.equals(value)))
841             index = i;
842         }
843     }
844     
845     if (index != -1)
846         this.removeElement(index, true);
847     else
848         index = 0;
849     
850     return index;
851     }
852     
853     public void removeValue(int index) {
854     if (index >= this.bindingsSize() &&
855         Common.isVetoable(this.type) && useVetoEvents()) {
856
857         DOMBinding b = (DOMBinding)this.bindings.get(index);
858         if (b != null) {
859         Object JavaDoc value = b.getValue(this);
860
861         // Check before doing any change if this is ok
862
this.raiseVetoableEvent(value, 0, OP_SETTER_REMOVE);
863         }
864         
865         // If we reach this point, no exception has been raised,
866
// and the change can happen.
867
}
868
869     this.removeElement(index, true);
870     }
871     
872     // Common sanity check on the parameters
873
private void checkParams(int index, Object JavaDoc value, boolean add) {
874     if (add) {
875         if (value == null)
876         throw new IllegalArgumentException JavaDoc(Common.
877             getMessage("CannotAddNullValue_msg"));
878     }
879     else {
880         if (!Common.isArray(this.type)) {
881         if (index > 0)
882             throw new IllegalArgumentException JavaDoc(Common.
883             getMessage("InvalidIndexForTypeProperty_msg"));
884         }
885         else {
886         if ((index < 0) || (index >= this.bindingsSize()))
887             throw new IndexOutOfBoundsException JavaDoc();
888         }
889     }
890     }
891     
892     /**
893      * Different cases might happen in this method, depending on the
894      * type of the property, if the property is an array or not,
895      * and also depending on the type of the value parameter.
896      *
897      * If the property is a single property, the value replaces the
898      * previous value.
899      *
900      * If the property is an indexed value, the operation is not allowed.
901      *
902      * If the propery didn't have any value (no DOM node yet), a DOMBinding
903      * is created and takes care of setting the new value.
904      *
905      */

906     protected int setElement(int index, Object JavaDoc value, boolean add) {
907     this.checkParams(index, value, add);
908     
909     if ((value != null) && Common.isBean(this.type)
910         && ((BaseBean)value).hasDomNode()) {
911         Document doc1 = null;
912         Document doc2 = null;
913         DOMBinding domBinding1 = ((BaseBean)value).domBinding();
914         DOMBinding domBinding2 = bean.domBinding();
915         //System.out.println("domBinding1="+domBinding1);
916
//System.out.println("domBinding2="+domBinding2);
917
if (domBinding1 != null && domBinding2 != null) {
918             Node node1 = domBinding1.getNode();
919             Node node2 = domBinding2.getNode();
920             //System.out.println("node1="+node1);
921
//System.out.println("node2="+node2);
922
if (node1 != null && node2 != null) {
923                 doc1 = node1.getOwnerDocument();
924                 doc2 = node2.getOwnerDocument();
925             }
926         }
927         //System.out.println("doc1="+doc1);
928
//System.out.println("doc2="+doc2);
929
//System.out.println("doc1==doc2="+(doc1==doc2));
930
if (doc1 != null && doc1 == doc2) {
931             //
932
// For now, reject any attempt to insert an element which
933
// is already part of the DOM graph.
934
// We could clone the element automatically, but for now,
935
// we just reject it.
936
//
937
throw new IllegalArgumentException JavaDoc(Common.
938                                                getMessage("CannotInsertElementAlreadyInGraph_msg"));
939         }
940     }
941
942     /*
943     // If String, store a copy of the string
944     if (value instanceof java.lang.String) {
945         
946         // An empty optional String is equivalement to a null string
947         if (!this.isBean()
948             && ((this.type & Common.MASK_INSTANCE) == Common.TYPE_0_1)
949             && value.equals("")) { // NOI18N
950             
951             value = null;
952         } else {
953             value = new String((String)value);
954         }
955     }
956     */

957     
958     if (DDLogFlags.debug) {
959         TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
960         DDLogFlags.DBG_BLD, 1, DDLogFlags.SETVALUE,
961         this.dtdName + "[" + index + "] - " +
962         (value==null?"null":value.toString()));
963     }
964     
965     //
966
// If the object is null, the setValue call is equivalent to a remove
967
// Otherwise, the object might be a bean or a wrapper object (String,
968
// Boolean, Date, ...)
969
//
970
// If the object is a bean, we have to remove first the previous bean.
971
// Then, we affect the new bean to the DOMBinding object.
972
// (See the comment in the delete() operation to see how this
973
// involves the BeanProp and DOMBinding data structure).
974
//
975
// If the object is a wrapper, simply call the DomBinding to change
976
// the DOM node value (there is no change in the internal structure).
977
//
978

979     // Do not send any event while updating
980
this.eventMgr.delay();
981     
982     if (value != null) {
983         // Before adding the new value, remove the previous one
984
if (Common.isBean(this.type) && !add)
985             this.removeElement(index, false);
986         
987         DOMBinding b = null;
988         boolean empty = true;
989         Object JavaDoc oldValue = null;
990         
991         if (!add) {
992             empty = (this.bindingsSize() == 0);
993             if (!empty)
994                 b = (DOMBinding)this.bindings.get(index);
995         }
996         
997         if (b == null) {
998             //
999
// This is a brand new property - create the DOMBinding
1000
// for this object. The DOMBinding will take care
1001
// later on to create the proper DOM tree structures.
1002
//
1003
b = new DOMBinding();
1004            b.register(this, value);
1005            b.setDefaultAttributeValues(this);
1006        }
1007        
1008        if (add)
1009            index = this.bindingsSize();
1010        
1011        if (empty)
1012            this.bindings.add(b);
1013        else
1014            this.bindings.set(index, b);
1015        
1016        if (DDLogFlags.debug) {
1017            TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
1018                            DDLogFlags.DBG_BLD, 1, DDLogFlags.NEWBIND,
1019                            this.dtdName + "[" + (empty?0:index) + "]");
1020        }
1021        
1022        oldValue = b.setValue(this, value);
1023        
1024        //
1025
// If the parent bean is attached to a DOM Node, this new
1026
// value has to be attached to the DOM Node. Otherwise,
1027
// the new value stays cached in the DOMBinding.
1028
// The algorithm is: if the parent is not anchored to the DOM
1029
// tree, cache the value. If the parent is attached,
1030
// flush into the DOM tree all the cached values.
1031
//
1032
if (this.bean.hasDomNode()) {
1033            b.syncNodes(this, new Action(Action.ADD));
1034        }
1035        else {
1036            if (DDLogFlags.debug) {
1037                TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
1038                                DDLogFlags.DBG_BLD, 1, DDLogFlags.CACHING,
1039                                this.dtdName);
1040            }
1041        }
1042        
1043        //
1044
// Now that the setValue is done, ask the DOMBinding to signal
1045
// all the properties bound to this Node (including ourself).
1046
//
1047
b.notifyBeansForChange(oldValue, value, null);
1048        
1049        // Attribute values might have been cached in the bean
1050
if (Common.isBean(this.type) && (value != null)) {
1051            BaseBean bb = (BaseBean)value;
1052            String JavaDoc[] names = bb.cachedAttributeNames();
1053            for (int i=0; i<names.length; i++) {
1054                this.setAttributeValue(index, names[i],
1055                                       bb.cachedAttributeValue(names[i]));
1056            }
1057            bb.cachedAttributeClear();
1058        }
1059    }
1060    else
1061        this.removeElement(index, false);
1062    
1063    // If anything changed, fire the events now
1064
this.eventMgr.fireEvents();
1065    
1066    return index;
1067    }
1068    
1069    
1070    /**
1071     * Tell the binding to remove the Node reference.
1072     */

1073    private void removeElement(int index, boolean remove) {
1074    DOMBinding b;
1075    
1076    if (index >= this.bindingsSize()) {
1077        // There is nothing here so do nothing.
1078
return;
1079    }
1080    
1081    b = (DOMBinding)this.bindings.get(index);
1082    if (b != null) {
1083        Object JavaDoc oldValue = b.getValue(this);
1084        b.setLastKnownIndex(this, index);
1085        
1086        PropertyChangeEvent e = this.createEvent(b, oldValue, null, null);
1087        
1088        if (DDLogFlags.debug) {
1089        TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
1090        DDLogFlags.DBG_EVT, 1, DDLogFlags.CREATEREM,
1091        (e==null?"no-event":e.getPropertyName()) +
1092        " - source " + this.beanName +
1093        "\n\t\toldValue is " +
1094        (oldValue==null?"<null>":"<"+
1095        oldValue.toString()+">") +
1096        "\n\t\tnewValue is null");
1097        }
1098        
1099        if (remove)
1100        this.bindings.remove(index);
1101        else
1102        this.bindings.set(index, null);
1103        
1104        // Remove and notify the subtree included ourself
1105
b.syncNodes(this, new Action(Action.REMOVE, e));
1106        
1107        // Notify the parents
1108
this.notifyInternal(e, true);
1109    }
1110    }
1111    
1112    // Return the list of well known values
1113
public Object JavaDoc[] knownValues() {
1114    if (this.knownValues == null)
1115        return null;
1116    
1117    int size = this.knownValues.size();
1118    Object JavaDoc a = Array.newInstance(this.propClass, size);
1119    
1120    for (int i=0; i<size; i++)
1121        Array.set(a, i, this.knownValues.get(i));
1122    
1123    return (Object JavaDoc[])a;
1124    }
1125    
1126    // Add a value to the list of well known defined values
1127
protected void addKnownValue(Object JavaDoc value) {
1128    if (this.knownValues == null)
1129        this.knownValues = new ArrayList();
1130    
1131    if (this.propClass.isAssignableFrom(value.getClass()))
1132        this.knownValues.add(value);
1133    }
1134    
1135    
1136    //
1137
// Values methods.
1138
//
1139
//////////////////
1140

1141    
1142    /////////////////////
1143
//
1144
// Attributes methods
1145
//
1146

1147    /**
1148     * Attributes definition live in the BeanProp object while attribute
1149     * values live in the DOMBinding objects.
1150     */

1151    public void createAttribute(String JavaDoc dtdName, String JavaDoc name, int type,
1152                String JavaDoc[] values, String JavaDoc defValue) {
1153    AttrProp ap = new AttrProp(this.dtdName, dtdName, name, type,
1154    values, defValue);
1155    this.attributes.add(ap);
1156    
1157    if (DDLogFlags.debug) {
1158        TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
1159        DDLogFlags.DBG_BLD, 5, DDLogFlags.CREATEATTR,
1160        this.beanName + ": " + ap.toString());
1161    }
1162    }
1163    
1164    /**
1165     * When the bean tree is built from an XML document and an attribute
1166     * not defined in the DTD is found, the attribute is dynamically added
1167     * to the list of defined attribute with the TRANSIENT status.
1168     */

1169    public void createTransientAttribute(String JavaDoc dtdName) {
1170    // Add the attribute only if we don't know about it
1171
AttrProp ap = this.getAttrProp(dtdName, true);
1172    
1173    if (ap != null)
1174        return;
1175    
1176    ap = new AttrProp(this.dtdName, dtdName, Common.convertName(dtdName),
1177    AttrProp.CDATA | AttrProp.IMPLIED |
1178    AttrProp.TRANSIENT, null, null);
1179    
1180    this.attributes.add(ap);
1181    
1182    if (DDLogFlags.debug) {
1183        TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
1184        DDLogFlags.DBG_BLD, 5, DDLogFlags.CREATEATTR,
1185        this.beanName + ": " + ap.toString());
1186    }
1187    }
1188    
1189    /**
1190     * Misc: convert the ArrayList into an array of AttrProp
1191     */

1192    public BaseAttribute[] getAttributes() {
1193    int size = this.attributes.size();
1194    AttrProp[] ret = new AttrProp[size];
1195    return (AttrProp[])this.attributes.toArray(ret);
1196    }
1197    
1198    /**
1199     * Misc: returns an array of the attribute names
1200     */

1201    public String JavaDoc[] getAttributeNames() {
1202    int size = this.attributes.size();
1203    String JavaDoc[] ret = new String JavaDoc[size];
1204    
1205    for (int i=0; i<size; i++) {
1206        AttrProp ap = (AttrProp)this.attributes.get(i);
1207        ret[i] = ap.getName();
1208    }
1209    return ret;
1210    }
1211    
1212    /**
1213     * Return the property name AttrProp
1214     */

1215    public AttrProp getAttrProp(String JavaDoc name, boolean quiet) {
1216    int size = this.attributes.size();
1217    
1218    for (int i=0; i<size; i++) {
1219        AttrProp ap = (AttrProp)this.attributes.get(i);
1220        if (ap.hasName(name))
1221        return ap;
1222    }
1223    
1224    if (!quiet)
1225        throw new IllegalArgumentException JavaDoc(Common.
1226        getMessage("UnknownAttributeForProperty_msg", name,
1227               this.beanName));
1228    else
1229        return null;
1230    }
1231    
1232    /**
1233     * Misc: return the property of name 'name'
1234     */

1235    public AttrProp getAttrProp(String JavaDoc name) {
1236    return this.getAttrProp(name, false);
1237    }
1238    
1239    /**
1240     * Set the value of the attribute
1241     */

1242    public void setAttributeValue(int index, String JavaDoc name, String JavaDoc value) {
1243    AttrProp ap = this.getAttrProp(name);
1244    
1245    this.checkParams(index, null, false);
1246
1247    // If it's fixed and they're changing it to a different value than the
1248
// default fixed value, then we've got an error. Note that the
1249
// comparison ought to be a value base comparison, and not a string
1250
// based comparison.
1251
if (ap.isFixed() && (ap.defaultValue == null || !ap.defaultValue.equals(value)))
1252        throw new IllegalStateException JavaDoc(Common.
1253        getMessage("CannotChangeFIXEDAttribute_msg"));
1254    
1255    if (ap.isEnum() && (value != null)) {
1256        String JavaDoc[] values = ap.getValues();
1257        boolean found = false;
1258        for (int i=0; i<values.length; i++) {
1259        if (values[i].equals(value)) {
1260            found = true;
1261            break;
1262        }
1263        }
1264        if (!found) {
1265        throw new IllegalArgumentException JavaDoc(Common.
1266            getMessage("ValueDoesNotMatchEnumValues_msg",
1267                   value, ap.enumsToString()));
1268        }
1269    }
1270    
1271    DOMBinding b = null;
1272    
1273    // The attribute value lives in the DOMBinding object
1274
if (index != 0 || this.bindingsSize() != 0) {
1275        b = (DOMBinding)this.bindings.get(index);
1276    } else if (DDLogFlags.debug) {
1277        System.err.println("What DOMBinding should I use for BeanProp.setAttributeValue?!?");
1278    }
1279    
1280    if (b != null) {
1281        Object JavaDoc oldValue = b.getAttributeValue(this, ap.getDtdName());
1282        b.setAttributeValue(this, ap.getDtdName(), normalizedAttrValue(value));
1283        
1284        // Notify about this attribute changed value
1285
b.notifyBeansForChange(oldValue, value, name);
1286    }
1287    }
1288    /** Checking attr value for correct chars, replace illegal chars with '?'
1289     */

1290    private String JavaDoc normalizedAttrValue(String JavaDoc value) {
1291        if (value==null) return null;
1292        StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
1293        for (int i=0;i<value.length();i++) {
1294            char ch = value.charAt(i);
1295            if (XMLUtil.isAttrContent((int)ch)) {
1296                sb.append(ch);
1297            } else {
1298                sb.append('?'); // illegal character replaced by '?'
1299
}
1300        }
1301        return sb.toString();
1302    }
1303    
1304    /**
1305     * Get the attribute value
1306     */

1307    public String JavaDoc getAttributeValue(int index, String JavaDoc name) {
1308    String JavaDoc ret = null;
1309    AttrProp ap = this.getAttrProp(name);
1310    
1311    if (!Common.isArray(this.type)) {
1312        // Value not set for single type property - return null
1313
if ((index > 0) || (this.bindingsSize() == 0))
1314        return null;
1315    }
1316    
1317    DOMBinding b = (DOMBinding)this.bindings.get(index);
1318    
1319    if (b != null)
1320        ret = b.getAttributeValue(this, ap.getDtdName());
1321    
1322    if (DDLogFlags.debug) {
1323        TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
1324        DDLogFlags.DBG_BLD, 10, DDLogFlags.GETATTR,
1325        this.beanName + "[" + index + "]." +
1326        ap.getDtdName() + ": " + ret + " " +
1327        ((b==null)?"<no binding>":("B("+b.hashCode()+")"))+
1328        " BP(" + this.hashCode() + ")");
1329    }
1330    
1331    return ret;
1332    }
1333    
1334    //
1335
// Attributes methods
1336
//
1337
///////////////////////
1338

1339    
1340    
1341    /**
1342     * Called back by the DOMBinding when another bean removed the binding.
1343     * This allows an automatic update of all the bean trees sharing the
1344     * same DOM nodes.
1345     */

1346    void removeBinding(DOMBinding binding) {
1347    throw new UnsupportedOperationException JavaDoc(Common.
1348        getMessage("NotImplementedYet_msg"));
1349    }
1350    
1351    
1352    /**
1353     * Create the DOM Nodes for all the properties previously cached.
1354     * (Cached because no beans/values created could be attached
1355     * to the real DOM tree: no element was attached to the DOM tree
1356     * at the time of their creation - see the setElement() method)
1357     */

1358    void syncNodes(Action a) {
1359    int size = this.bindingsSize();
1360    
1361    for (int i=0; i<size; i++) {
1362        DOMBinding b = (DOMBinding)this.bindings.get(i);
1363        if (b != null)
1364        b.syncNodes(this, a);
1365    }
1366    }
1367    
1368    /**
1369     * This method is recursively called to build the full path name
1370     * of a property.
1371     *
1372     * The full path name starts at the root of the graph and consists in:
1373     *
1374     * - the concatenation of each property name from the root
1375     * - a unique identification index for each indexed property
1376     *
1377     * For example /Book/Chapter.23a/Paragraph.34c/Comment
1378     *
1379     * The unique identifier is the hexadecimal representation of the
1380     * unique integer identifier of the DOM Binding, which is independant
1381     * from the position of the property in the array of the indexed prop.
1382     * If the array change, the name still represents the same object,
1383     * as long as every DOMBinding object of the path exists.
1384     */

1385    void buildPathName(DOMBinding binding, StringBuffer JavaDoc str) {
1386    if (binding == null)
1387        return;
1388    
1389    if (Common.isArray(this.type)) {
1390        DOMBinding b = null;
1391        int size = this.bindingsSize();
1392        int index = binding.getLastKnownIndex(this);
1393        
1394        for (int i=0; i<size; i++) {
1395        b = (DOMBinding)this.bindings.get(i);
1396        if (b == binding)
1397            break;
1398        else
1399            b = null;
1400        }
1401        
1402        //
1403
// Used when an element is deleted: we store its index
1404
// since we won't be able to find it later from
1405
// its unique id
1406
//
1407
if (index != -1) {
1408        // We didn't find any index - mark as removed with the
1409
// last known index
1410
str.insert(0, "i" + index); // NOI18N
1411
}
1412        
1413        //
1414
// We might not find the original index of the element if it has
1415
// been already removed. Use the default value -1 in this case.
1416
// Note that this value doesn't really matter since the
1417
// index parameter value (prefixed with 'i') is the meaningful
1418
// value in this situation.
1419
//
1420
if (b != null)
1421        str.insert(0, b.idToString());
1422        else
1423        str.insert(0, "-1"); // NOI18N
1424

1425        // Unique id for the index
1426
str.insert(0, "."); // NOI18N
1427
}
1428    str.insert(0, this.beanName);
1429    str.insert(0, "/"); // NOI18N
1430

1431    if (!this.isRoot && (this.bean != null))
1432        this.bean.buildPathName(str);
1433    }
1434    
1435    /**
1436     * Create a change event for the current property
1437     */

1438    PropertyChangeEvent createEvent(DOMBinding b, Object JavaDoc oldValue,
1439                    Object JavaDoc newValue, String JavaDoc attrName) {
1440    if (!useEvents() && !useVetoEvents())
1441        return null;
1442    
1443    StringBuffer JavaDoc name = new StringBuffer JavaDoc();
1444    
1445    if (attrName != null) {
1446        name.append(":"); // NOI18N
1447
name.append(attrName);
1448    }
1449    
1450    this.buildPathName(b, name);
1451    
1452    return new PropertyChangeEvent(this.bean, name.toString(),
1453                    oldValue, newValue);
1454    }
1455    
1456    /**
1457     * When a DOMBinding has changed (either DOM Node modification
1458     * or DOMBinding cached value change), the DOMBinding signals
1459     * any property bound to itself about the change.
1460     * This is where the event notification take place.
1461     *
1462     * Note that the full path name of the property is used as the
1463     * property name of the event.
1464     */

1465    PropertyChangeEvent prepareForChangeEvent(DOMBinding b, Object JavaDoc oldValue,
1466                         Object JavaDoc newValue, String JavaDoc attrName) {
1467    if (!useEvents())
1468        return null;
1469    
1470    PropertyChangeEvent e =
1471        this.createEvent(b, oldValue, newValue, attrName);
1472    
1473    if (DDLogFlags.debug) {
1474        TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
1475        DDLogFlags.DBG_EVT, 1, DDLogFlags.CREATECHG,
1476        (e==null?"no-event":e.getPropertyName()) +
1477        " - source " + this.beanName +
1478        "\n\t\toldValue is " +
1479        (oldValue==null?"<null>":"<"+
1480        oldValue.toString()+">") +
1481        "\n\t\tnewValue is " +
1482        (newValue==null?"<null>":"<"+
1483        newValue.toString()+">"));
1484    }
1485    return e;
1486    }
1487    
1488    // Helper method
1489
void notifyInternal(PropertyChangeEvent e, boolean propagate) {
1490    this.notifyInternal(new InternalEvent(InternalEvent.CHANGED, e),
1491    propagate);
1492    }
1493    
1494    // Mechanism used to propagate an event through the tree up to the root
1495
void notifyInternal(InternalEvent ie, boolean propagate) {
1496    if (ie.type == InternalEvent.CHANGED) {
1497        if (!useEvents())
1498        return;
1499        
1500        if (this.eventMgr.isDelayed()) {
1501        this.eventMgr.addEvent(ie.getPropertyChangeEvent(), propagate);
1502        return;
1503        }
1504        
1505        if (DDLogFlags.debug) {
1506        TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
1507        DDLogFlags.DBG_EVT, 1, DDLogFlags.NOTIFYCHG,
1508        ie.getPropertyChangeEvent().getPropertyName()+
1509        (propagate?"(P)":"") +
1510        " in " + this.beanName + " - " +
1511        (this.changeListeners==null?"null listener":
1512            (this.changeListeners.hasListeners(null)?
1513            "has listeners":"no listener")) );
1514        }
1515        
1516        if (this.changeListeners != null)
1517        // Notify the object listening on this property
1518
this.changeListeners.firePropertyChange(
1519            ie.getPropertyChangeEvent());
1520    }
1521    else
1522        if (ie.type == InternalEvent.VETOABLE) {
1523        if( !useVetoEvents())
1524            return;
1525        
1526        if (DDLogFlags.debug) {
1527            TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
1528            DDLogFlags.DBG_EVT, 1, DDLogFlags.NOTIFYVETO,
1529            ie.getPropertyChangeEvent().getPropertyName()+
1530            (propagate?"(P)":"") +
1531            " in " + this.beanName + " - " +
1532            (this.vetoableListeners==null?"null listener":
1533            (this.vetoableListeners.hasListeners(null)?
1534            "has listeners":"no listener")) );
1535        }
1536        
1537        try {
1538            if (this.vetoableListeners != null)
1539            // Notify the object listening on this property
1540
this.vetoableListeners.fireVetoableChange(
1541                ie.getPropertyChangeEvent());
1542        }
1543        catch(PropertyVetoException ve) {
1544            // This has been vetoed, raise our internal runtime exception
1545
// holding this ve. We'll rethrow it later on, from the
1546
// generated beans.
1547
throw new BaseProperty.VetoException(ve,
1548            Common.getMessage("ChangeForPropertyVetoed_msg",
1549                      this.beanName));
1550        }
1551        }
1552    
1553    // Notify the parent (this will go up to the root)
1554
if (!this.isRoot && (this.bean != null) && propagate)
1555        this.bean.notifyInternal(ie);
1556    }
1557    
1558    private void raiseVetoableEvent(Object JavaDoc value, int index, int op) {
1559    Object JavaDoc curValue = null;
1560    Object JavaDoc newValue = null;
1561    
1562    // Get the current and new value
1563
if (Common.isArray(this.type)) {
1564        Object JavaDoc []arrValue;
1565        
1566        curValue = this.getValues();
1567        switch(op) {
1568        case OP_SETTER_SETARRAY:
1569            // The new value is the new array
1570
newValue = value;
1571            break;
1572        case OP_SETTER_SETELT:
1573            // One element only will change
1574
arrValue = this.getObjectArray(0);
1575            arrValue[index] = value;
1576            newValue = arrValue;
1577            break;
1578        case OP_SETTER_ADD:
1579            // Add the new element at the end of the array
1580
arrValue = this.getObjectArray(1);
1581            arrValue[this.bindingsSize()] = value;
1582            newValue = arrValue;
1583            break;
1584        case OP_SETTER_REMOVE:
1585            // One element removed
1586

1587            // This call only allocate the array (no values)
1588
int i, j;
1589            Object JavaDoc[] curValues = (Object JavaDoc[])curValue;
1590            arrValue = this.getObjectArray(-1);
1591            
1592            for (i=0; i<curValues.length; i++) {
1593            if (curValues[i].equals(value))
1594                break;
1595            }
1596            
1597            if (i < curValues.length) {
1598            // We found it
1599
for (i=0, j=0; i<curValues.length; i++)
1600                if (!curValues[i].equals(value))
1601                arrValue[j++] = curValues[i];
1602            
1603            }
1604            else {
1605            arrValue = curValues;
1606            }
1607            
1608            newValue = arrValue;
1609            break;
1610        }
1611    }
1612    else {
1613        curValue = this.getValue(0);
1614        newValue = value;
1615    }
1616    
1617    // Create the event and wait for an exception
1618
PropertyChangeEvent e = this.createEvent(this.bean.binding,
1619                    curValue, newValue, null);
1620    
1621    if (DDLogFlags.debug) {
1622        TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
1623        DDLogFlags.DBG_EVT, 1, DDLogFlags.VETOABLE,
1624        (e==null?"no-event":e.getPropertyName()) +
1625        " - source " + this.beanName +
1626        "\n\t\toldValue is " +
1627        (curValue==null?"<null>":"<"+
1628        curValue.toString()+">") +
1629        "\n\t\tnewValue is " +
1630        (newValue==null?"<null>":"<"+
1631        newValue.toString()+">"));
1632    }
1633    
1634    // Propagate this vetoable event up to the root
1635
this.notifyInternal(new InternalEvent(InternalEvent.VETOABLE, e),
1636                true);
1637    }
1638    
1639    
1640    /**
1641     * Book-keeping of the listeners
1642     */

1643    public void addPCListener(PropertyChangeListener l) {
1644    handleEvents = true;
1645    if (this.changeListeners == null) {
1646        Object JavaDoc obj = (this.bean==null)?(Object JavaDoc)this:(Object JavaDoc)this.bean;
1647        this.changeListeners = new PropertyChangeSupport(obj);
1648    }
1649    this.changeListeners.addPropertyChangeListener(l);
1650    }
1651    
1652    public void removePCListener(PropertyChangeListener l) {
1653    if (this.changeListeners != null)
1654        this.changeListeners.removePropertyChangeListener(l);
1655    }
1656    
1657    public void addVCListener(VetoableChangeListener l) {
1658    if (this.checkVetoable(true)) {
1659        handleVetoEvents = true;
1660        if (this.vetoableListeners == null) {
1661        Object JavaDoc obj = (this.bean==null)?(Object JavaDoc)this:(Object JavaDoc)this.bean;
1662        this.vetoableListeners = new VetoableChangeSupport(obj);
1663        }
1664        this.vetoableListeners.addVetoableChangeListener(l);
1665    }
1666    }
1667    
1668    public void removeVCListener(VetoableChangeListener l) {
1669    if (this.checkVetoable(false))
1670        this.vetoableListeners.removeVetoableChangeListener(l);
1671    }
1672    
1673    
1674    //
1675
// A property might not support the veto mechanism either because
1676
// it has not been generated to support it, or because it is a bean.
1677
//
1678
private boolean checkVetoable(boolean raise) {
1679    if (Common.isVetoable(this.type) || Common.isBean(this.type))
1680        return true;
1681    
1682    if (raise) {
1683        if (!Common.isBean(this.type)) {
1684        throw new Schema2BeansRuntimeException(Common.
1685            getMessage("PropertyDoesntSupportVeto_msg", this.beanName));
1686        }
1687    }
1688    
1689    return false;
1690    }
1691    
1692    /**
1693     * If the current property is a bean, returns an instance of the bean
1694     */

1695    BaseBean newBeanInstance() {
1696    if (Common.isBean(this.type)) {
1697        try {
1698        Constructor c = null;
1699        
1700        try {
1701            Class JavaDoc[] cc = new Class JavaDoc[] {int.class};
1702            c = this.propClass.getDeclaredConstructor(cc);
1703        }
1704        catch(NoSuchMethodException JavaDoc me) {
1705            return (BaseBean)this.propClass.newInstance();
1706        }
1707        
1708        // Do not initialize the default values
1709
Object JavaDoc[] p =
1710            new Object JavaDoc[] {new Integer JavaDoc(Common.NO_DEFAULT_VALUES)};
1711        
1712        return (BaseBean)c.newInstance(p);
1713        }
1714        catch(Exception JavaDoc e) {
1715        TraceLogger.error(e);
1716        throw new Schema2BeansRuntimeException(Common.
1717            getMessage("CantInstantiateBean_msg", e.getMessage()));
1718        }
1719    }
1720    return null;
1721    }
1722    
1723    /**
1724     * This method is called to associate this bean property to a DOM Node.
1725     * When the bean graph is built from the DOM graph, the DOM graph is
1726     * parsed while trying to match the element names of the DOM graph
1727     * with the property names of the bean graph.
1728     *
1729     * When a match is found between a bean property name and a DOM element
1730     * name, this method is called to associate the DOM node to the bean.
1731     *
1732     * The bean property might be a simple String (#PCDATA in DTD) as the
1733     * leaf of the bean graph, or a bean object, containing other beans.
1734     * (One node of the bean graph). Whatever the case is, there is a DOM Node
1735     * that correspond to this property.
1736     *
1737     * Also, the propery might be either a single value property or an
1738     * array. In the first case, only one Node of the DOM graph can match
1739     * the property (if not, this is fatal error in building the bean graph).
1740     * In the second case, this property contains an array referencing the
1741     * DOM graph.
1742     *
1743     * Note that the property might be of type: optional, mandatory,
1744     * an array possibly empty or an array with at least one element.
1745     * The verify() method might be called to check the consistency of the
1746     * type of the property, especially after the bean graph is built or
1747     * before a modified bean graph has to be saved.
1748     *
1749     * See also the DOMBinding class comments.
1750     */

1751    public DOMBinding registerDomNode(Node node, DOMBinding binding,
1752                      BaseBean bean) throws Schema2BeansException {
1753    int count = 0;
1754    int size = this.bindingsSize();
1755    
1756    // Check that we don't already know this node
1757
for (int i=0; i<size; i++) {
1758        DOMBinding b = (DOMBinding)this.bindings.get(i);
1759        if ((b.getNode() == node) || (binding == b))
1760        throw new Schema2BeansException(Common.
1761            getMessage("NodeAlreadyReferenced_msg", node));
1762        
1763        if (b.getNode() != null) {
1764        count++;
1765        }
1766    }
1767    
1768    
1769    // Check we can accept the new node
1770
if (count==0 || Common.isArray(this.type)) {
1771        //
1772
// Where is only one DOMBinding object per node. Use it if
1773
// it already exists. When we are building the first bean
1774
// graph, all DOMBindings are now objects. However, any
1775
// view on the bean graph which is already created,
1776
// uses an existing DOMBinding.
1777
//
1778
if (binding == null)
1779        binding = new DOMBinding(node);
1780        
1781        if (bean == null)
1782        bean = this.newBeanInstance();
1783        
1784        binding.register(this, bean);
1785        binding.setValue(this, bean);
1786        
1787        this.bindings.add(binding);
1788    }
1789    else {
1790        if (DDLogFlags.debug) {
1791        TraceLogger.put(TraceLogger.DEBUG,
1792        TraceLogger.SVC_DD,
1793        DDLogFlags.DBG_BLD, 1,
1794        DDLogFlags.EXCEEDED,
1795        "exceeding capacity for " + this.dtdName +
1796        "(not an array)");
1797        }
1798        // A property of type 0_1 or 1 can be bound to only one DOM Node.
1799
throw new Schema2BeansException(Common.
1800        getMessage("PropertyAlreadyBoundToDOMNode_msg", this.dtdName));
1801    }
1802    
1803    return binding;
1804    }
1805    
1806    public String JavaDoc toString() {
1807    return this.dtdName;
1808    }
1809    
1810
1811    public boolean isRoot() {
1812    return this.isRoot;
1813    }
1814    
1815    // BeanProperty implementation
1816
public String JavaDoc getName() {
1817    return this.beanName;
1818    }
1819    
1820    public String JavaDoc getDtdName() {
1821    return this.dtdName;
1822    }
1823    
1824    public boolean isIndexed() {
1825    return Common.isArray(this.type);
1826    }
1827    
1828    public Class JavaDoc getPropertyClass() {
1829    return this.propClass;
1830    }
1831    
1832    public boolean isBean() {
1833    return Common.isBean(this.type);
1834    }
1835    
1836    public int size() {
1837    return this.bindingsSize();
1838    }
1839    
1840    public String JavaDoc getFullName(int index) {
1841        return this.buildFullName(index, null);
1842    }
1843    
1844    public String JavaDoc getFullName() {
1845        return this.buildFullName(0, null);
1846    }
1847    
1848    String JavaDoc buildFullName(int index, String JavaDoc attr) {
1849        StringBuffer JavaDoc name = new StringBuffer JavaDoc();
1850    
1851        if (!Common.isArray(this.type)) {
1852            // Value not set for single type property - return null
1853
if ((index > 0) || (this.bindingsSize() == 0))
1854                return null;
1855        }
1856    
1857        DOMBinding b = (DOMBinding)this.bindings.get(index);
1858    
1859        if (b != null) {
1860            if (attr != null) {
1861                name.append(":"); // NOI18N
1862
name.append(attr);
1863            }
1864            this.buildPathName(b, name);
1865        }
1866        else
1867            return null;
1868    
1869        return name.toString();
1870    }
1871    
1872    public int getInstanceType() {
1873    return (this.type & Common.MASK_INSTANCE);
1874    }
1875    
1876    public boolean isChoiceProperty() {
1877    return (this.group != null);
1878    }
1879    
1880    public BaseProperty[] getChoiceProperties() {
1881    if (this.isChoiceProperty()) {
1882        return this.group.list();
1883    }
1884    return null;
1885    }
1886    
1887    // true if the name is either the mangled name or the dtd name
1888
public boolean hasName(String JavaDoc name) {
1889    if (name.equals(this.beanName) || name.equals(this.dtdName))
1890        return true;
1891    else
1892        return false;
1893    }
1894
1895    public boolean isKey() {
1896    return Common.isKey(this.type);
1897    }
1898}
1899
1900
1901
Popular Tags