KickJava   Java API By Example, From Geeks To Geeks.

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


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.beans.*;
24 import org.w3c.dom.*;
25 import java.text.MessageFormat JavaDoc;
26
27 // To dynamically instanciate a wrapper object
28
import java.lang.reflect.*;
29
30
31 /**
32  * The DOMBinding class binds the bean properties of the bean graph to
33  * the nodes of the DOM graph. There is one DOMBinding object for
34  * one DOM node.
35  *
36  * A bean property never accesses a DOM node directly, but always through
37  * a DOMBinding object. In one bean graph, only one property will be linked
38  * to a DOMBinding object. However, if another graph is created for the same
39  * DOM graph, two bean properties will share the same DOM node using the
40  * DOMBinding object.
41  *
42  * As the DOMBinding object might be shared amoung different bean properties,
43  * it keeps a list of all the properties using it. This is how, for example,
44  * events can be fired to the beans of different graphs referencing a same
45  * node, when its value is changed.
46  *
47  * The beans of the bean graph lives there.
48  */

49 public class DOMBinding {
50
51     // This integer uniquely identify this BeanProp in the graph
52
int id;
53
54     Node node;
55
56     // This is used by the BeanProp.setter(obj[]) to optimize its parsing
57
int pos;
58     // The same purpose but for ordering the DOM nodes
59
int posDOM;
60
61     private static final Class JavaDoc charArrayClass =
62     java.lang.reflect.Array.newInstance(java.lang.Character.TYPE, 0).getClass();
63
64     class BeanProperty {
65         //boolean changed;
66
// BeanProp object containing the property
67
BeanProp beanProp;
68
69     // Null if the property is a String.
70
Object JavaDoc value;
71
72     // Last known real index into the BeanProp array before the removal
73
int lastIndex;
74
75     ArrayList attributes;
76
77     BeanProperty(BeanProp b) {
78         this.beanProp = b;
79         this.value = null;
80         //this.changed = false;
81
this.lastIndex = -1;
82         this.attributes = null;
83     }
84     }
85
86     class CacheAttr {
87         String JavaDoc name;
88         String JavaDoc value;
89
90         CacheAttr(String JavaDoc name, String JavaDoc value) {
91             this.name = name;
92             this.value = value;
93         }
94     }
95
96     private BeanProperty prop;
97
98     public DOMBinding() {
99         this.id = DDFactory.getUniqueId();
100     }
101
102     public DOMBinding(Node node) {
103         this();
104         // DOM node this DOMBinding refers to
105
this.node = node;
106     }
107
108     void setNode(Node node) {
109         this.node = node;
110     }
111
112     void moveBefore(BeanProp prop, Node node) {
113         Node parent = prop.getParentNode();
114
115         parent.removeChild(this.node);
116         parent.insertBefore(this.node, node);
117     }
118
119     public int getId() {
120     return this.id;
121     }
122
123     String JavaDoc idToString() {
124     return Integer.toHexString(this.id);
125     }
126
127     Node getNode() {
128     return this.node;
129     }
130
131     /**
132      */

133     void register(BeanProp prop, Object JavaDoc value) {
134     BeanProperty bp = new BeanProperty(prop);
135
136     if (Common.isBean(prop.type))
137         ((BaseBean)value).setDomBinding(this);
138
139     this.prop = bp;
140
141     if (DDLogFlags.debug) {
142         TraceLogger.put(TraceLogger.DEBUG,
143                 TraceLogger.SVC_DD,
144                 DDLogFlags.DBG_BLD, 1,
145                 DDLogFlags.BINDPROP,
146                 "property " + prop.getDtdName() +
147                 " bound to B(" + this.hashCode() + ")");
148     }
149
150     //
151
// Following is a little trick to deal with attribute that are not
152
// defined in the dtd. When we register this new element, we ask
153
// for all the attributes and add them dynamically, as transient,
154
// to the BeanProp list of attributes.
155
//
156
if (this.node != null) {
157         NamedNodeMap l = this.node.getAttributes();
158         for (int i=0; i<l.getLength(); i++) {
159         Node n = l.item(i);
160         prop.createTransientAttribute(n.getNodeName());
161         }
162     }
163     }
164
165     /**
166      * Look in the list of BeanProperty, the one corresponding
167      * to the BeanProp object.
168      */

169     private BeanProperty getBeanProperty(BeanProp prop) {
170     if (this.prop != null && this.prop.beanProp == prop)
171         return this.prop;
172     else
173         return null;
174     }
175
176
177     /**
178      * Returns the property BeanProp object associated to the bean object
179      * instance bean. Return null if not found.
180      */

181     BeanProp getBeanProp(BaseBean bean) {
182     if (this.prop != null && this.prop.value == bean)
183         return this.prop.beanProp;
184     else
185         return null;
186     }
187
188     /**
189      * Used when we remove a property to set the index it used to be
190      * in the BeanProp bindings array. This is how when we build on event
191      * that reference an element which has been removed we know about
192      * its previous index.
193      */

194     void setLastKnownIndex(BeanProp prop, int index) {
195     BeanProperty bp = this.getBeanProperty(prop);
196     if (bp != null)
197         bp.lastIndex = index;
198     }
199
200     /**
201      * Retreive the last known index stored before removal.
202      */

203     int getLastKnownIndex(BeanProp prop) {
204     BeanProperty bp = this.getBeanProperty(prop);
205
206     if (bp != null)
207         return bp.lastIndex;
208     else
209         return -1;
210     }
211
212     /**
213      * This method returns the bean associated to the BeanProp object.
214      * Since there can be only one Node per BeanProp (single content as
215      * one bean or one distinct Node per each element if indexed prop),
216      * there is only one entry for this BeanProp object.
217      *
218      * The object returned can be either a bean or a wrapper object.
219      */

220     Object JavaDoc getBean(BeanProp prop) {
221     BeanProperty bp = this.getBeanProperty(prop);
222
223     if (bp != null)
224         return bp.value;
225     else
226         return null;
227     }
228
229     /**
230      * Return the value of the attribute. Get the value from the DOM Node
231      * or from the cache, depending on the existance of the DOM Node.
232      */

233     String JavaDoc getAttributeValue(BeanProp prop, String JavaDoc name) {
234         if (this.node != null) {
235             // Get the value from the DOM Node
236
Attr a = ((Element)this.node).getAttributeNode(name);
237             if (a != null)
238                 return a.getValue();
239             else
240                 return null;
241         } else {
242             // Get the value from the cache
243
BeanProperty bp = this.getBeanProperty(prop);
244             if (bp != null && bp.attributes != null) {
245                 CacheAttr ca = findCacheAttr(bp, name);
246                 if (ca != null)
247                     return ca.value;
248             }
249         }
250         return null;
251     }
252
253     private CacheAttr findCacheAttr(BeanProperty bp, String JavaDoc name) {
254         for (int i = 0; i < bp.attributes.size(); i++) {
255             CacheAttr ca = (CacheAttr)bp.attributes.get(i);
256             if (ca.name.equals(name))
257                 return ca;
258         }
259         return null;
260     }
261
262     /**
263      * Return the value of the attribute. Get the value from the DOM Node
264      * or from the cache, depending on the existance of the DOM Node.
265      */

266     void setAttributeValue(BeanProp prop, String JavaDoc name, String JavaDoc value) {
267         if (this.node != null) {
268             if (value != null)
269                 ((Element)this.node).setAttribute(name, value);
270             else {
271                 String JavaDoc v = ((Element)this.node).getAttribute(name);
272                 if (v != null) {
273                     //
274
// An empty string might either mean that the attribute
275
// has an empty value or that the attribute is not
276
// defined at all (ID type for example).
277
// The following tries to remove it and ignore the
278
// fact that the attribute might not be defined.
279
//
280
try {
281                         ((Element)this.node).removeAttribute(name);
282                     } catch(DOMException e) {
283                         // Ignore it
284
}
285                 }
286             }
287         } else {
288             // There is no DOM Node, cache the value
289
BeanProperty bp = this.getBeanProperty(prop);
290             if (bp.attributes == null)
291                 bp.attributes = new ArrayList();
292             CacheAttr ca = findCacheAttr(bp, name);
293             if (ca == null) {
294                 ca = new CacheAttr(name, value);
295                 bp.attributes.add(ca);
296             } else {
297                 ca.value = value;
298             }
299         }
300     }
301
302     /**
303      * Fill the attribute with the default values - this is typically
304      * called when a new property is created, before it is synced into
305      * a DOM Node. This makes the attributes cached and when the property
306      * is turned into a DOM node, the attributes are also created.
307      */

308     void setDefaultAttributeValues(BeanProp prop) {
309     BeanProperty bp = this.getBeanProperty(prop);
310     if (bp != null) {
311         // Set the default values
312
BaseAttribute[] ap = prop.getAttributes();
313         for (int i=0; i<ap.length; i++) {
314         String JavaDoc value = ap[i].getDefaultValue();
315         if (value != null)
316             this.setAttributeValue(prop, ap[i].getDtdName(), value);
317         }
318     }
319     }
320
321     /**
322      * Get the value of the DOM node
323      */

324     public String JavaDoc getDomValue(Node n) {
325     StringBuffer JavaDoc str = new StringBuffer JavaDoc();
326     this.nodeToString(str, n, true);
327     return str.toString();
328     }
329
330     // Method related to XmlToString (see DDFactory)
331
private void nodeToString(StringBuffer JavaDoc str, Node n, boolean root) {
332     if (root)
333         // Don't go for siblings on the root
334
nodeChildrenToString(str, n);
335     else {
336         for (;n != null; n = n.getNextSibling())
337         nodeChildrenToString(str, n);
338     }
339     }
340
341
342     // Method related to XmlToString (see DDFactory)
343
private void nodeChildrenToString(StringBuffer JavaDoc str, Node n) {
344     String JavaDoc value = n.getNodeValue();
345     short type = n.getNodeType();
346
347     if ((type == Node.TEXT_NODE || type == Node.CDATA_SECTION_NODE) && (value != null))
348         str.append(value);
349
350     if (n.getFirstChild() != null)
351         nodeToString(str, n.getFirstChild(), false);
352     }
353
354
355     /**
356      * Return the value of the property prop. If the property is of type
357      * String, we have to read the value from the DOM Node. If the
358      * type is bean, we just have to return the bean associated to the
359      * DOM Node (we don't have to access the DOM graph in this case).
360      */

361     Object JavaDoc getValue(BeanProp prop) {
362         String JavaDoc ret = null;
363     switch(prop.getType() & Common.MASK_TYPE) {
364         case Common.TYPE_STRING:
365         //
366
// If this binding is not attached to a DOM tree yet, return
367
// the cached value.
368
//
369
Class JavaDoc cls = prop.getPropClass();
370
371         if (this.node != null)
372             ret = this.getDomValue(this.node);
373         else
374             ret = (String JavaDoc)this.getBean(prop);
375
376         //System.out.println("cls="+cls+" node="+node+" ret="+ret);
377
if (!(java.lang.String JavaDoc.class).isAssignableFrom(cls)
378             && (ret != null)) {
379             String JavaDoc clsName = cls.getName().intern();
380             //
381
// Build an instance of the wrapper class (STRING type
382
// but not a String class). The wrapper must have a
383
// String constructor or implements the Wrapper interface.
384
//
385

386             try {
387                 // If cls implements Wrapper, use it first
388
if ((Wrapper.class).isAssignableFrom(cls)) {
389                     Wrapper w = (Wrapper)cls.newInstance();
390                     w.setWrapperValue(ret);
391                     return w;
392                 }
393                 //
394
// Not very nice but we want to support the
395
// Character/char case and there is no String
396
// constructor for this core class.
397
//
398
if ((java.lang.Character JavaDoc.class).isAssignableFrom(cls)) {
399                     String JavaDoc s = ret.trim();
400                     char c = '\0';
401                     if (s.length() == 0) {
402                         if (ret.length() != 0)
403                             c = ret.charAt(0);
404                     }
405                     else
406                         c = s.charAt(0);
407
408                     return new Character JavaDoc(c);
409                 }
410                 if (charArrayClass.isAssignableFrom(cls))
411                     return ret.toCharArray();
412                 if (clsName == "org.netbeans.modules.schema2beans.QName"
413                     || clsName == "javax.xml.namespace.QName") {
414                     String JavaDoc ns = "";
415                     String JavaDoc localPart = null;
416                     String JavaDoc prefix = "";
417                     int colonPos = ret.indexOf(':');
418                     if (colonPos < 0) {
419                         localPart = ret;
420                     } else {
421                         prefix = ret.substring(0, colonPos);
422                         localPart = ret.substring(colonPos+1, ret.length());
423                         ns = findNamespace(prefix);
424                     }
425                     //System.out.println("localPart="+localPart+" ns="+ns+" prefix="+prefix);
426
if (clsName == "org.netbeans.modules.schema2beans.QName") {
427                         return new
428                             org.netbeans.modules.schema2beans.QName(ns,
429                                                                     localPart,
430                                                                     prefix);
431                     }
432                     Constructor c =
433                         cls.getDeclaredConstructor(new Class JavaDoc[] {String JavaDoc.class,
434                                                                 String JavaDoc.class,
435                                                                 String JavaDoc.class});
436                     return c.newInstance(new Object JavaDoc[] {ns, localPart, prefix});
437                 }
438                 return JavaBeansUtil.convertValue(cls, ret.trim());
439             } catch(Exception JavaDoc e) {
440                 //TraceLogger.error(e);
441
throw new Schema2BeansRuntimeException(
442                              MessageFormat.format(Common.getMessage(
443                                          "CantInstantiatePropertyClass_msg"),
444                                          new Object JavaDoc[] {cls.getName(), prop.getName(),
445                                                         ret, e.getLocalizedMessage()}), e);
446             }
447         }
448         return ret;
449
450         case Common.TYPE_BEAN:
451         return this.getBean(prop);
452         //case BeanProp.TYPE_BOOLEAN:
453
case Common.TYPE_BOOLEAN:
454             return nodeToBoolean(prop);
455         default:
456             throw new Schema2BeansRuntimeException(Common.getMessage(
457                           "TypeNotSupported_msg",
458                           prop.getPropClass(), new Integer JavaDoc(prop.getType())));
459     }
460     }
461
462     protected Boolean JavaDoc nodeToBoolean(BeanProp prop) {
463         // If there is a node, that means the empty node exists (true)
464
// If there is no node set, we might just not be attached to
465
// a parent node right now, check our prop
466
//System.out.println("node="+node+" getBean(prop)="+this.getBean(prop));
467
if (node == null) {
468             Object JavaDoc result = getBean(prop);
469             if (result == null)
470                 return Boolean.FALSE;
471             //System.out.println("!!! node was null, but prop has something. prop="+prop);
472
return (Boolean JavaDoc) result;
473         } else {
474             String JavaDoc ret = getDomValue(node);
475             //System.out.println("ret="+ret);
476
if (ret == null)
477                 return Boolean.TRUE;
478             ret = ret.toLowerCase().intern();
479             if (ret == "false" || ret == "0")
480                 return Boolean.FALSE;
481             // Just the node being there and not saying false is enough to
482
// be true.
483
return Boolean.TRUE;
484         }
485     }
486
487     protected String JavaDoc findNamespace(String JavaDoc prefix) {
488         String JavaDoc targetName = "xmlns:"+prefix;
489         for (Node n = node; n != null; n = n.getParentNode()) {
490             NamedNodeMap nodeMap = n.getAttributes();
491             if (nodeMap == null)
492                 continue;
493             Attr a = (Attr) nodeMap.getNamedItem(targetName);
494             if (a != null) {
495                 return a.getValue();
496             }
497         }
498         return "";
499     }
500
501     /**
502      * Return the value as a String of the object
503      */

504     private String JavaDoc getWrapperValue(Object JavaDoc value) {
505         if (value.getClass().isInstance(Wrapper.class))
506             return ((Wrapper)value).getWrapperValue();
507         else if (value.getClass().isAssignableFrom(charArrayClass)) {
508             //System.out.println("It's a char[]");
509
return new String JavaDoc((char[])value);
510         } else if (value instanceof java.util.Calendar JavaDoc) {
511             return calendarToString((java.util.Calendar JavaDoc) value);
512         } else if (value instanceof org.netbeans.modules.schema2beans.QName) {
513             org.netbeans.modules.schema2beans.QName q =
514                 (org.netbeans.modules.schema2beans.QName) value;
515             if ("".equals(q.getPrefix()))
516                 return q.getLocalPart();
517             else
518                 return q.getPrefix() + ":" + q.getLocalPart();
519         } else {
520             Class JavaDoc cls = value.getClass();
521             String JavaDoc clsName = cls.getName();
522             if (clsName.equals("javax.xml.namespace.QName")) {
523                 try {
524                     Method prefixMethod = cls.getDeclaredMethod("getPrefix",
525                                                                 new Class JavaDoc[0]);
526                     String JavaDoc prefix = (String JavaDoc) prefixMethod.invoke(value,
527                                                                  new Object JavaDoc[0]);
528                     Method localPartMethod = cls.getDeclaredMethod("getLocalPart",
529                                                                    new Class JavaDoc[0]);
530                     String JavaDoc localPart = (String JavaDoc) localPartMethod.invoke(value,
531                                                                        new Object JavaDoc[0]);
532                     if ("".equals(prefix))
533                         return localPart;
534                     else
535                         return prefix + ":" + localPart;
536                 } catch (java.lang.NoSuchMethodException JavaDoc e) {
537                     throw new RuntimeException JavaDoc(e);
538                 } catch (java.lang.IllegalAccessException JavaDoc e) {
539                     throw new RuntimeException JavaDoc(e);
540                 } catch (java.lang.reflect.InvocationTargetException JavaDoc e) {
541                     throw new RuntimeException JavaDoc(e);
542                 }
543             }
544             return value.toString();
545         }
546     }
547
548     public static String JavaDoc calendarToString(java.util.Calendar JavaDoc cal) {
549         java.util.Date JavaDoc date = cal.getTime();
550         java.text.SimpleDateFormat JavaDoc formatter;
551         if (cal.get(java.util.Calendar.HOUR) == 0 && cal.get(java.util.Calendar.MINUTE) == 0 && cal.get(java.util.Calendar.SECOND) == 0) {
552             formatter = new java.text.SimpleDateFormat JavaDoc("yyyy-MM-dd"); // NOI18N
553
} else if (cal.get(java.util.Calendar.MILLISECOND) == 0) {
554             formatter = new java.text.SimpleDateFormat JavaDoc("yyyy-MM-dd'T'HH:mm:ss"); // NOI18N
555
} else {
556             formatter = new java.text.SimpleDateFormat JavaDoc("yyyy-MM-dd'T'HH:mm:ss.S"); // NOI18N
557
}
558         String JavaDoc result = formatter.format(date);
559         if (java.util.TimeZone.getDefault().hasSameRules(cal.getTimeZone())) {
560             return result;
561         }
562                 java.util.TimeZone JavaDoc tz = cal.getTimeZone();
563         int offset = timeZoneOffset(tz, 0);
564         if (offset == 0)
565             return result+"Z";
566         int seconds = offset / 1000;
567         if (seconds > 0) {
568             result += "+";
569         } else {
570             seconds = -1 * seconds;
571             result += "-";
572         }
573         int hours = seconds / 3600;
574         if (hours < 10)
575             result += "0";
576         result += hours + ":";
577         int minutes = (seconds / 60) % 60;
578         if (minutes < 10)
579             result += "0";
580         result += minutes;
581         return result;
582     }
583
584     /**
585      * The value is only stored locally. A later call to syncNodes()
586      * updates the DOM nodes with this local value.
587      */

588     Object JavaDoc setValue(BeanProp prop, Object JavaDoc value) {
589     Object JavaDoc oldValue = null;
590     BeanProperty bp = this.getBeanProperty(prop);
591
592     if (bp != null) {
593         oldValue = bp.value;
594
595         // Use the value cache, otherwise get the Node tree value
596
if ((oldValue == null) && (this.node != null))
597             oldValue = this.getValue(prop);
598
599         if (Common.isBean(prop.type))
600             bp.value = value;
601         else
602             if (Common.isString(prop.type) && (value != null)) {
603                 if (value instanceof org.netbeans.modules.schema2beans.QName) {
604                     org.netbeans.modules.schema2beans.QName q =
605                         (org.netbeans.modules.schema2beans.QName) value;
606                     String JavaDoc prefix = q.getPrefix();
607                     String JavaDoc declaredNS = "";
608                     if ("".equals(prefix)) {
609                         prefix = prop.getDtdName()+"_ns__";
610                         q = new org.netbeans.modules.schema2beans.QName(q.getNamespaceURI(),
611                                                     q.getLocalPart(),
612                                                     prefix);
613                     } else {
614                         declaredNS = findNamespace(prefix);
615                     }
616                     if ("".equals(declaredNS)) {
617                         // It's undeclared, so declare it.
618
((Element)node).setAttribute("xmlns:"+prefix,
619                                                      q.getNamespaceURI());
620                         prop.createTransientAttribute("xmlns:"+prefix);
621                     }
622                 } else {
623                     Class JavaDoc cls = value.getClass();
624                     String JavaDoc clsName = cls.getName();
625                     if (clsName.equals("javax.xml.namespace.QName")) {
626                         try {
627                         Method prefixMethod = cls.getDeclaredMethod("getPrefix",
628                                                                 new Class JavaDoc[0]);
629                         String JavaDoc prefix = (String JavaDoc) prefixMethod.invoke(value,
630                                                                  new Object JavaDoc[0]);
631                         Method nsMethod = cls.getDeclaredMethod("getNamespaceURI",
632                                                                 new Class JavaDoc[0]);
633                         String JavaDoc ns = (String JavaDoc) nsMethod.invoke(value,
634                                                              new Object JavaDoc[0]);
635                         String JavaDoc declaredNS = "";
636                         if ("".equals(prefix)) {
637                             Method localPartMethod = cls.getDeclaredMethod("getLocalPart",
638                                                                    new Class JavaDoc[0]);
639                             String JavaDoc localPart = (String JavaDoc) localPartMethod.invoke(value,
640                                                                        new Object JavaDoc[0]);
641                             Constructor c = cls.getDeclaredConstructor(new Class JavaDoc[] {String JavaDoc.class, String JavaDoc.class, String JavaDoc.class});
642                             prefix = prop.getDtdName()+"_ns__";
643                             value = c.newInstance(new Object JavaDoc[] {ns, localPart,
644                                                                 prefix});
645                         } else {
646                             declaredNS = findNamespace(prefix);
647                         }
648                         if ("".equals(declaredNS)) {
649                             // It's undeclared, so declare it.
650
((Element)node).setAttribute("xmlns:"+prefix,
651                                                          ns);
652                             prop.createTransientAttribute("xmlns:"+prefix);
653                         }
654                         } catch (java.lang.NoSuchMethodException JavaDoc e) {
655                             throw new RuntimeException JavaDoc(e);
656                         } catch (java.lang.IllegalAccessException JavaDoc e) {
657                             throw new RuntimeException JavaDoc(e);
658                         } catch (java.lang.InstantiationException JavaDoc e) {
659                             throw new RuntimeException JavaDoc(e);
660                         } catch (java.lang.reflect.InvocationTargetException JavaDoc e) {
661                             throw new RuntimeException JavaDoc(e);
662                         }
663                     }
664                 }
665                 bp.value = this.getWrapperValue(value);
666             } else
667                 bp.value = value;
668     }
669
670     return oldValue;
671     }
672
673     /** Workaround for TimeZone.getOffset which is not in JDK1.3 */
674     private static int timeZoneOffset (java.util.TimeZone JavaDoc tz, long date) {
675     if (tz.inDaylightTime(new Date(date))) {
676         return tz.getRawOffset() + (tz.useDaylightTime()? 3600000: 0);
677     }
678     return tz.getRawOffset();
679     }
680
681
682     /**
683      * Removes the reference to the property prop and delete the DOM Nodes.
684      *
685      */

686     void remove(BeanProp prop) {
687     }
688
689
690     void removeProp(BeanProp prop) {
691     if (this.prop != null && this.prop.beanProp == prop)
692         this.prop = null;
693     }
694
695     // Remove a DOM node
696
void removeNode(BeanProp prop) {
697     if (this.node != null) {
698         Node parent = prop.getParentNode();
699
700         if (DDLogFlags.debug) {
701         TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
702                 DDLogFlags.DBG_BLD, 1,
703                 DDLogFlags.DELETENODE,
704                 this.node.getNodeName() + " from " +
705                 parent.getNodeName());
706         }
707         removeSurroundingSpace(parent, node);
708         parent.removeChild(this.node);
709         this.node = null;
710     }
711     }
712
713     /**
714      * Removes space surrounding node appointed to removal to keep proper document layout
715      * @param parent
716      * @param node
717      */

718     private static void removeSurroundingSpace(Node parent, Node node) {
719         Node nextNode = node.getNextSibling();
720         if (nextNode != null && nextNode.getNodeType() == Node.TEXT_NODE) {
721             String JavaDoc s = nextNode.getNodeValue();
722             if (s.trim().length() == 0) {
723                 int i = s.indexOf('\n');
724                 if (i == -1) {
725                     parent.removeChild(nextNode);
726                 } else {
727                     s = s.substring(i);
728                     Node previousNode = node.getPreviousSibling();
729                     if (previousNode != null && previousNode.getNodeType() == Node.TEXT_NODE) {
730                         String JavaDoc s1 = previousNode.getNodeValue();
731                         if (previousNode.getPreviousSibling() != null) {
732                             if (s1.trim().length() == 0) {
733                                 i = s1.lastIndexOf('\n');
734                                 if (i > 0) {
735                                     s = s1.substring(0, i) + s;
736                                 } else {
737                                     parent.removeChild(previousNode);
738                                 }
739                             }
740                         } else {
741                             parent.removeChild((previousNode));
742                         }
743                     }
744                     nextNode.setNodeValue(s);
745                 }
746             }
747         }
748     }
749
750     /**
751      * The BeanProp that changed the value calls the DOMBinding after
752      * the setValue in order to propagate the Changed event to all the
753      * BeanProp that share this same Node.
754      */

755     void notifyBeansForChange(Object JavaDoc oldValue, Object JavaDoc newValue,
756                   String JavaDoc attrName) {
757
758     if (this.prop != null) {
759         PropertyChangeEvent e = this.prop.beanProp.
760         prepareForChangeEvent(this, oldValue, newValue, attrName);
761
762         this.prop.beanProp.notifyInternal(e, true);
763     }
764     }
765
766     /**
767      * This method is called when time has come to update the DOM nodes
768      * with the local value of the DOMBinding.
769      * Typically, this happens when the user updates a final value of the
770      * bean tree.
771      *
772      * This method is called when a property has been set and the bean
773      * holding this property is already attached to a DOM Node. Only
774      * this condition allows the value to be flushed into the DOM tree.
775      *
776      */

777     void syncNodes(BeanProp prop, BeanProp.Action a) {
778         BeanProperty bp = this.getBeanProperty(prop);
779
780         if (DDLogFlags.debug) {
781             TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
782                             DDLogFlags.DBG_BLD, 1,
783                             DDLogFlags.SYNCNODES,
784                             a.toString() + " " + prop.getDtdName() +
785                             (bp==null?" - unknown prop!":""));
786         }
787
788         if (bp == null)
789             return;
790
791         if(a.action == a.REMOVE) {
792             int i = prop.idToIndex(this.id);
793             if (i != -1)
794                 bp.lastIndex = i;
795
796             PropertyChangeEvent e =
797                 prop.prepareForChangeEvent(this, bp.value, null, null);
798
799             if (Common.isBean(prop.type)) {
800                 // Recurse on all the properties of the bean
801
BaseBean bean = ((BaseBean)bp.value);
802                 bean.syncNodes(a);
803             }
804             //System.out.println("this.prop="+this.prop+" this.prop.beanProp="+this.prop.beanProp+" this.prop.beanProp==prop "+(this.prop.beanProp==prop)+" this.prop.value="+this.prop.value+" bp.value="+bp.value);
805
if (this.prop != null && this.prop.beanProp==prop) {
806                 // See IZ#19802
807
if (node != null &&
808                     (prop.getType() & Common.MASK_TYPE) == Common.TYPE_STRING) {
809                     // Since it's a String, the value is stored in the DOM
810
// graph, and not in our BeanProperty. Stash the contents
811
// of the DOM node into our BeanProperty,
812
// before we remove the node and lose the value.
813
bp.value = getDomValue(node);
814                 }
815                 //this.removeProp(prop);
816
}
817             this.removeNode(prop);
818             prop.notifyInternal(e, false);
819         }
820         else
821             if(a.action == a.ADD) {
822                 if (Common.isBean(prop.type)) {
823                     NodeFactory f = prop.getNodeFactory();
824
825                     if (this.node != null) {
826                         System.out.println("Removing from old graph.");
827                         BeanProp.Action a2;
828                         a2 = new BeanProp.Action(a.REMOVE);
829                         syncNodes(this.prop.beanProp, a2);
830                         /*
831                         throw new IllegalStateException(Common.getMessage(
832                                      "DOMBindingAlreadyHasNode_msg",
833                                      node.toString()));
834                         */

835                     }
836
837                     Node parent = prop.getParentNode();
838                     this.node = f.createElement(prop);
839
840                     if (DDLogFlags.debug) {
841                         TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
842                                         DDLogFlags.DBG_BLD, 1,
843                                         DDLogFlags.SYNCING,
844                                         "adding new child " +
845                                         this.node.getNodeName() +
846                                         " to node " + parent.getNodeName());
847                     }
848
849                     Node sibling = prop.getFollowingSibling(this);
850                     parent.insertBefore(this.node, sibling);
851
852                     // Recurse the syncNodes on all the properties of the bean
853
BaseBean bean = ((BaseBean)bp.value);
854                     bean.setGraphManager(prop.bean.graphManager());
855                     bean.syncNodes(a);
856                 } else if (Common.isBoolean(prop.type)) {
857                     boolean v = false;
858
859                     if (bp.value != null)
860                         v = ((Boolean JavaDoc)bp.value).booleanValue();
861
862                     if (Common.shouldNotBeEmpty(prop.type) || node == null ||
863                         (nodeToBoolean(prop)).booleanValue() != v) {
864                         // Current node and expected value are not the same
865
if (DDLogFlags.debug) {
866                             TraceLogger.put(TraceLogger.DEBUG,
867                                             TraceLogger.SVC_DD,
868                                             DDLogFlags.DBG_BLD, 1,
869                                             DDLogFlags.SYNCING,
870                                             (v?"adding new":"removing") +
871                                             " tag " +
872                                             prop.getDtdName());
873                         }
874
875                         Node parent = prop.getParentNode();
876                         if (v || Common.shouldNotBeEmpty(prop.type)) {
877                             NodeFactory f = prop.getNodeFactory();
878                             if (node == null) {
879                                 node = f.createElement(prop);
880                                 Node sibling = prop.getFollowingSibling(this);
881                                 parent.insertBefore(this.node, sibling);
882                             }
883                             if (Common.shouldNotBeEmpty(prop.type)) {
884                                 CharacterData text =
885                                     (CharacterData) node.getFirstChild();
886                                 if (text == null) {
887                                     text = (CharacterData)f.createText();
888                                     node.appendChild(text);
889                                     if (DDLogFlags.debug) {
890                                         TraceLogger.put(TraceLogger.DEBUG,
891                                                         TraceLogger.SVC_DD,
892                                                         DDLogFlags.DBG_BLD, 1,
893                                                         DDLogFlags.SYNCING,
894                                                         "adding new text node " +
895                                                         text.getNodeName() +
896                                                         " to node " +
897                                                         this.node.getNodeName());
898                                     }
899                                 }
900                                 text.setData(""+v);
901                             }
902                         } else if (node != null) {
903                             parent.removeChild(this.node);
904                             this.node = null;
905                         }
906                     }
907                     else {
908                         if (DDLogFlags.debug) {
909                             TraceLogger.put(TraceLogger.DEBUG,
910                                             TraceLogger.SVC_DD,
911                                             DDLogFlags.DBG_BLD, 1,
912                                             DDLogFlags.SYNCING,
913                                             "keeping same boolean value");
914                         }
915                     }
916                 } else {
917                     NodeFactory f = prop.getNodeFactory();
918                     if (this.node == null) {
919                         Node parent = prop.getParentNode();
920                         this.node = f.createElement(prop);
921
922                         if (DDLogFlags.debug) {
923                             TraceLogger.put(TraceLogger.DEBUG,
924                                             TraceLogger.SVC_DD,
925                                             DDLogFlags.DBG_BLD, 1,
926                                             DDLogFlags.SYNCING,
927                                             "adding new child " +
928                                             this.node.getNodeName() +
929                                             " to node " +
930                                             parent.getNodeName());
931                         }
932
933                         Node sibling = prop.getFollowingSibling(this);
934                         parent.insertBefore(this.node, sibling);
935                     }
936
937                     CharacterData text =
938                         (CharacterData)this.node.getFirstChild();
939
940                     if (text == null) {
941                         text = (CharacterData)f.createText();
942                         this.node.appendChild(text);
943                         if (DDLogFlags.debug) {
944                             TraceLogger.put(TraceLogger.DEBUG,
945                                             TraceLogger.SVC_DD,
946                                             DDLogFlags.DBG_BLD, 1,
947                                             DDLogFlags.SYNCING,
948                                             "adding new text node " +
949                                             text.getNodeName() +
950                                             " to node " +
951                                             this.node.getNodeName());
952                         }
953                     }
954
955                     text.setData(bp.value.toString());
956                 }
957
958                 // Add any attribute cached for this new node
959
if (this.node != null) {
960                     if (bp.attributes != null) {
961                         for (int i=0; i<bp.attributes.size(); i++) {
962                             CacheAttr ca = (CacheAttr)bp.attributes.get(i);
963                             // null value means, remove.
964
// Ignore remove for a new node.
965
if (ca.value != null)
966                                 ((Element)this.node).setAttribute(ca.name, ca.value);
967                         }
968                         bp.attributes = null;
969                     }
970                 }
971             }
972             else
973                 throw new IllegalArgumentException JavaDoc(Common.getMessage(
974                                                                      "UnknownAction_msg", new Integer JavaDoc(a.action)));
975     }
976
977     boolean hasDomNode() {
978     return (this.node != null);
979     }
980 }
981
982
983
Popular Tags