alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.modules.schema2beans;

import java.util.*;
import java.io.*;
import java.beans.*;
import org.w3c.dom.*;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;

// To dynamically instanciate a wrapper object
import java.lang.reflect.*;


/**
 *  The DOMBinding class binds the bean properties of the bean graph to
 *  the nodes of the DOM graph. There is one DOMBinding object for
 *  one DOM node.
 *
 *  A bean property never accesses a DOM node directly, but always through
 *  a DOMBinding object. In one bean graph, only one property will be linked
 *  to a DOMBinding object. However, if another graph is created for the same
 *  DOM graph, two bean properties will share the same DOM node using the
 *  DOMBinding object.
 *
 *  As the DOMBinding object might be shared amoung different bean properties,
 *  it keeps a list of all the properties using it. This is how, for example,
 *  events can be fired to the beans of different graphs referencing a same
 *  node, when its value is changed.
 *
 *  The beans of the bean graph lives there.
 */
public class DOMBinding {

    //	This integer uniquely identify this BeanProp in the graph
    int			id;
    
    Node		node;
    
    //	This is used by the BeanProp.setter(obj[]) to optimize its parsing
    int			pos;
    //	The same purpose but for ordering the DOM nodes
    int			posDOM;
    
    private static final Class charArrayClass = 
	java.lang.reflect.Array.newInstance(java.lang.Character.TYPE, 0).getClass();
    
    class BeanProperty {
	boolean 	changed;
	// BeanProp object containing the property
	BeanProp	beanProp;
	
	// Null if the property is a String.
	Object		value;
	
	// Last known real index into the BeanProp array before the removal
	int		lastIndex;
	
	ArrayList	attributes;
	
	BeanProperty(BeanProp b) {
	    this.beanProp = b;
	    this.value = null;
	    this.changed = false;
	    this.lastIndex = -1;
	    this.attributes = null;
	}
    }
    
    class CacheAttr {
        String name;
        String value;
	
        CacheAttr(String name, String value) {
            this.name = name;
            this.value = value;
        }
    }
    
    private BeanProperty prop;
    
    public DOMBinding() {
        this.id = DDFactory.getUniqueId();
    }
    
    public DOMBinding(Node node) {
        this();
        // DOM node this DOMBinding refers to
        this.node = node;
    }
    
    void setNode(Node node) {
        this.node = node;
    }
    
    void moveBefore(BeanProp prop, Node node) {
        Node parent = prop.getParentNode();
	
        parent.removeChild(this.node);
        parent.insertBefore(this.node, node);
    }
    
    public int getId() {
	return this.id;
    }
    
    String idToString() {
	return Integer.toHexString(this.id);
    }
    
    Node getNode() {
	return this.node;
    }
    
    /**
     */
    void register(BeanProp prop, Object value) {
	BeanProperty bp = new BeanProperty(prop);
	
	if (Common.isBean(prop.type))
	    ((BaseBean)value).setDomBinding(this);
	
	this.prop = bp;
	
	if (DDLogFlags.debug) {
	    TraceLogger.put(TraceLogger.DEBUG,
			    TraceLogger.SVC_DD,
			    DDLogFlags.DBG_BLD, 1,
			    DDLogFlags.BINDPROP,
			    "property " + prop.getDtdName() +
			    " bound to B(" + this.hashCode() + ")");
	}
	
	//
	// Following is a little trick to deal with attribute that are not
	// defined in the dtd. When we register this new element, we ask
	// for all the attributes and add them dynamically, as transient,
	// to the BeanProp list of attributes.
	//
	if (this.node != null) {
	    NamedNodeMap l = this.node.getAttributes();
	    for (int i=0; i 0) {
			result += "+";
		} else {
			seconds = -1 * seconds;
			result += "-";
		}
		int hours = seconds / 3600;
		if (hours < 10)
            result += "0";
		result += hours + ":";
		int minutes = (seconds / 60) % 60;
		if (minutes < 10)
            result += "0";
		result += minutes;
		return result;
	}
    
    /**
     *	The value is only stored locally. A later call to syncNodes()
     *	updates the DOM nodes with this local value.
     */
    Object setValue(BeanProp prop, Object value) {
	Object		oldValue = null;
	BeanProperty 	bp = this.getBeanProperty(prop);
	
	if (bp != null) {
	    oldValue = bp.value;
	    
	    //	Use the value cache, otherwise get the Node tree value
	    if ((oldValue == null) && (this.node != null))
            oldValue = this.getValue(prop);
	    
	    if (Common.isBean(prop.type))
            bp.value = value;
	    else
            if (Common.isString(prop.type) && (value != null)) {
                if (value instanceof org.netbeans.modules.schema2beans.QName) {
                    org.netbeans.modules.schema2beans.QName q =
                        (org.netbeans.modules.schema2beans.QName) value;
                    String prefix = q.getPrefix();
                    String declaredNS = "";
                    if ("".equals(prefix)) {
                        prefix = prop.getDtdName()+"_ns__";
                        q = new org.netbeans.modules.schema2beans.QName(q.getNamespaceURI(),
                                                    q.getLocalPart(),
                                                    prefix);
                    } else {
                        declaredNS = findNamespace(prefix);
                    }
                    if ("".equals(declaredNS)) {
                        // It's undeclared, so declare it.
                        ((Element)node).setAttribute("xmlns:"+prefix,
                                                     q.getNamespaceURI());
                        prop.createTransientAttribute("xmlns:"+prefix);
                    }
                } else {
                    Class cls = value.getClass();
                    String clsName = cls.getName();
                    if (clsName.equals("javax.xml.namespace.QName")) {
                        try {
                        Method prefixMethod = cls.getDeclaredMethod("getPrefix",
                                                                new Class[0]);
                        String prefix = (String) prefixMethod.invoke(value,
                                                                 new Object[0]);
                        Method nsMethod = cls.getDeclaredMethod("getNamespaceURI",
                                                                new Class[0]);
                        String ns = (String) nsMethod.invoke(value,
                                                             new Object[0]);
                        String declaredNS = "";
                        if ("".equals(prefix)) {
                            Method localPartMethod = cls.getDeclaredMethod("getLocalPart",
                                                                   new Class[0]);
                            String localPart = (String) localPartMethod.invoke(value,
                                                                       new Object[0]);
                            Constructor c = cls.getDeclaredConstructor(new Class[] {String.class, String.class, String.class});
                            prefix = prop.getDtdName()+"_ns__";
                            value = c.newInstance(new Object[] {ns, localPart,
                                                                prefix});
                        } else {
                            declaredNS = findNamespace(prefix);
                        }
                        if ("".equals(declaredNS)) {
                            // It's undeclared, so declare it.
                            ((Element)node).setAttribute("xmlns:"+prefix,
                                                         ns);
                            prop.createTransientAttribute("xmlns:"+prefix);
                        }
                        } catch (java.lang.NoSuchMethodException e) {
                            throw new RuntimeException(e);
                        } catch (java.lang.IllegalAccessException e) {
                            throw new RuntimeException(e);
                        } catch (java.lang.InstantiationException e) {
                            throw new RuntimeException(e);
                        } catch (java.lang.reflect.InvocationTargetException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
                bp.value = this.getWrapperValue(value);
            } else
                bp.value = value;
	}
	
	return oldValue;
    }
    
    /** Workaround for TimeZone.getOffset which is not in JDK1.3 */
    private static int timeZoneOffset (java.util.TimeZone tz, long date) {
	if (tz.inDaylightTime(new Date(date))) {
	    return tz.getRawOffset() + (tz.useDaylightTime()? 3600000: 0);
	}
	return tz.getRawOffset();
    }

    
    /**
     *	Removes the reference to the property prop and delete the DOM Nodes.
     *
     */
    void remove(BeanProp prop) {
    }
    
    
    void removeProp(BeanProp prop) {
	if (this.prop != null && this.prop.beanProp == prop)
	    this.prop = null;
    }

    //	Remove a DOM node
    void removeNode(BeanProp prop) {
	if (this.node != null) {
	    Node parent = prop.getParentNode();
	    
	    if (DDLogFlags.debug) {
		TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
				DDLogFlags.DBG_BLD, 1,
				DDLogFlags.DELETENODE,
				this.node.getNodeName() + " from " +
				parent.getNodeName());
	    }
	    
	    parent.removeChild(this.node);
	    this.node = null;
	}
    }
    
    /**
     *	The BeanProp that changed the value calls the DOMBinding after
     *	the setValue in order to propagate the Changed event to all the
     *	BeanProp that share this same Node.
     */
    void notifyBeansForChange(Object oldValue, Object newValue, 
			      String attrName) {

	if (this.prop != null) {
	    PropertyChangeEvent e = this.prop.beanProp.
		prepareForChangeEvent(this, oldValue, newValue, attrName);
	    
	    this.prop.beanProp.notifyInternal(e, true);
	}
    }
    
    /**
     *  This method is called when time has come to update the DOM nodes
     *  with the local value of the DOMBinding.
     *  Typically, this happens when the user updates a final value of the
     *  bean tree.
     *
     *  This method is called when a property has been set and the bean
     *  holding this property is already attached to a DOM Node. Only
     *  this condition allows the value to be flushed into the DOM tree.
     *
     */
    void syncNodes(BeanProp prop, BeanProp.Action a) {
        BeanProperty 	bp = this.getBeanProperty(prop);
	
        if (DDLogFlags.debug) {
            TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
                            DDLogFlags.DBG_BLD, 1,
                            DDLogFlags.SYNCNODES,
                            a.toString() + " " + prop.getDtdName() +
                            (bp==null?" - unknown prop!":""));
        }
	
        if (bp == null)
            return;
	
        if(a.action == a.REMOVE) {
            int i = prop.idToIndex(this.id);
            if (i != -1)
                bp.lastIndex = i;
	    
            PropertyChangeEvent e =
                prop.prepareForChangeEvent(this, bp.value, null, null);
	    
            if (Common.isBean(prop.type)) {
                //  Recurse on all the properties of the bean
                BaseBean bean = ((BaseBean)bp.value);
                bean.syncNodes(a);
            }
            //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);
            if (this.prop != null && this.prop.beanProp==prop) {
                // See IZ#19802
                if (node != null &&
                    (prop.getType() & Common.MASK_TYPE) == Common.TYPE_STRING) {
                    // Since it's a String, the value is stored in the DOM
                    // graph, and not in our BeanProperty.  Stash the contents
                    // of the DOM node into our BeanProperty,
                    // before we remove the node and lose the value.
                    bp.value = getDomValue(node);
                }
                //this.removeProp(prop);
            }
            this.removeNode(prop);
            prop.notifyInternal(e, false);
        }
        else
            if(a.action == a.ADD) {
                if (Common.isBean(prop.type)) {
                    NodeFactory f = prop.getNodeFactory();
		    
                    if (this.node != null) {
                        System.out.println("Removing from old graph.");
                        BeanProp.Action a2;
                        a2 = new BeanProp.Action(a.REMOVE);
                        syncNodes(this.prop.beanProp, a2);
                        /*
                        throw new IllegalStateException(Common.getMessage(
                                     "DOMBindingAlreadyHasNode_msg",
                                     node.toString()));
                        */
                    }
		    
                    Node parent = prop.getParentNode();
                    this.node = f.createElement(prop);
		    
                    if (DDLogFlags.debug) {
                        TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
                                        DDLogFlags.DBG_BLD, 1,
                                        DDLogFlags.SYNCING,
                                        "adding new child " +
                                        this.node.getNodeName() +
                                        " to node " + parent.getNodeName());
                    }
		    
                    Node sibling = prop.getFollowingSibling(this);
                    parent.insertBefore(this.node, sibling);
		    
                    //  Recurse the syncNodes on all the properties of the bean
                    BaseBean bean = ((BaseBean)bp.value);
                    bean.setGraphManager(prop.bean.graphManager());
                    bean.syncNodes(a);
                } else if (Common.isBoolean(prop.type)) {
                    boolean v = false;
			
                    if (bp.value != null)
                        v = ((Boolean)bp.value).booleanValue();

                    if (Common.shouldNotBeEmpty(prop.type) || node == null ||
                        (nodeToBoolean(prop)).booleanValue() != v) {
                        //  Current node and expected value are not the same
                        if (DDLogFlags.debug) {
                            TraceLogger.put(TraceLogger.DEBUG,
                                            TraceLogger.SVC_DD,
                                            DDLogFlags.DBG_BLD, 1,
                                            DDLogFlags.SYNCING,
                                            (v?"adding new":"removing") +
                                            " tag " +	
                                            prop.getDtdName());
                        }
			    
                        Node parent = prop.getParentNode();
                        if (v || Common.shouldNotBeEmpty(prop.type)) {
                            NodeFactory f = prop.getNodeFactory();
                            if (node == null) {
                                node = f.createElement(prop);
                                Node sibling = prop.getFollowingSibling(this);
                                parent.insertBefore(this.node, sibling);
                            }
                            if (Common.shouldNotBeEmpty(prop.type)) {
                                CharacterData text =
                                    (CharacterData) node.getFirstChild();
                                if (text == null) {
                                    text = (CharacterData)f.createText();
                                    node.appendChild(text);
                                    if (DDLogFlags.debug) {
                                        TraceLogger.put(TraceLogger.DEBUG,
                                                        TraceLogger.SVC_DD,
                                                        DDLogFlags.DBG_BLD, 1,
                                                        DDLogFlags.SYNCING,
                                                        "adding new text node " +
                                                        text.getNodeName() +
                                                        " to node " +
                                                        this.node.getNodeName());
                                    }
                                }
                                text.setData(""+v);
                            }
                        } else if (node != null) {
                            parent.removeChild(this.node);
                            this.node = null;
                        }
                    }
                    else {
                        if (DDLogFlags.debug) {
                            TraceLogger.put(TraceLogger.DEBUG,
                                            TraceLogger.SVC_DD,
                                            DDLogFlags.DBG_BLD, 1,
                                            DDLogFlags.SYNCING,
                                            "keeping same boolean value");
                        }
                    }
                } else {
                    NodeFactory f = prop.getNodeFactory();
                    if (this.node == null) {
                        Node parent = prop.getParentNode();
                        this.node = f.createElement(prop);
			    
                        if (DDLogFlags.debug) {
                            TraceLogger.put(TraceLogger.DEBUG,
                                            TraceLogger.SVC_DD,
                                            DDLogFlags.DBG_BLD, 1,
                                            DDLogFlags.SYNCING,
                                            "adding new child " +
                                            this.node.getNodeName() +
                                            " to node " +
                                            parent.getNodeName());
                        }
			    
                        Node sibling = prop.getFollowingSibling(this);
                        parent.insertBefore(this.node, sibling);
                    }
			
                    CharacterData text =
                        (CharacterData)this.node.getFirstChild();
			
                    if (text == null) {
                        text = (CharacterData)f.createText();
                        this.node.appendChild(text);
                        if (DDLogFlags.debug) {
                            TraceLogger.put(TraceLogger.DEBUG,
                                            TraceLogger.SVC_DD,
                                            DDLogFlags.DBG_BLD, 1,
                                            DDLogFlags.SYNCING,
                                            "adding new text node " +
                                            text.getNodeName() +
                                            " to node " +
                                            this.node.getNodeName());
                        }
                    }
			
                    text.setData(bp.value.toString());
                }
		
                //  Add any attribute cached for this new node
                if (this.node != null) {
                    if (bp.attributes != null) {
                        for (int i=0; i
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.