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.lang.reflect.Constructor;

/**
 *  This class is the base class for any generated bean. This class provides
 *  the property book-keeping and access as a set of dynamic creation/accessor
 *  property methods.
 *
 *  BaseBean is the class that any generated schema2beans class extends. The
 *  generated code implements the specific accessors methods while the BaseBean
 *  class provides the generic ones. For example, a generated bean would have
 *
 *    public String getDescription() {
 *        return (String)this.getValue(DESCRIPTION);
 *    }
 *
 *  BaseBean handles any value as an Object, where the generated schema2beans
 *  class handles a specific class/scalar type depending on its corresponding
 *  DTD element (and optional mdd values. See user document for mdd info).
 *
 *  The idea of BaseBean is to provide the unique schema2beans java bean on
 *  which all generated schema2beans class rely. This is the user entry point
 *  of the schema2beans runtime. This is where you find all the generic methods
 *  that apply to all schema2beans classes: merge, find, clone, get/set attributes,
 *  etc.
 *
 *  If we had to draw a gereric overview of the schema2beans architecture, we would
 *  draw three parts: BaseBean, BeanProp and DOMBinding. This is where most
 *  of the schema2beans runtime is implemented.
 *
 *  There is one BaseBean instance per schema2beans node (one non final element
 *  of the DTD has one generated class that extends BaseBean).
 *  Each BaseBean holds its properties as BeanProp objects. For each property,
 *  BaseBean has one instance of BeanProp. For example, if we have as a DTD:
 *
 *  
 *  
 *  
 *
 *  The we would have two classes that would extend BaseBean: Book and Chapter.
 *  The book BaseBean would handle two instance of BeanProp, one for the
 *  property chapter and one of summary.
 *  The BeanProp for summary would handle a single String property, and the
 *  BeanProp for chapter would handle an indexed property of Chapter classes.
 *  Also, the chapter BaseBean (one instance for each element of the indexed
 *  property), would have one instance of BeanProp, that would handle the 
 *  String indexed property line.
 *
 *  The internal graph representation of the XML document is handled using
 *  a DOM tree. DOMBinding is the class that takes care of linking the
 *  BeanProp instances to the DOM nodes.
 *
 */
public abstract class BaseBean implements Cloneable, Bean {
    
    /*
     *	Because schema2beans doesn't generate user information about the
     *	relationship between the properties, BaseBean provides some
     *	method to give the user this information.
     */
    public class IterateChoiceProperties implements java.util.Iterator {
        private ArrayList groups;
        private int index;
	
        public IterateChoiceProperties() {
            this.groups = new ArrayList();
            this.index = 0;
        }
	
        void add(BeanProp prop) {
            if (prop.group != null && !this.groups.contains(prop.group))
                this.groups.add(prop.group);
        }
	
        public boolean hasNext() {
            return (this.index < this.groups.size());
        }
	
        public Object next() throws NoSuchElementException {
            if (hasNext()) {
                BeanProp.GroupProp gp =
                    (BeanProp.GroupProp)this.groups.get(this.index++);
                return (BaseProperty[])gp.list();
            }
            else
                throw new NoSuchElementException();
        }
	
        public void remove()
            throws UnsupportedOperationException, IllegalStateException {
            throw new UnsupportedOperationException();
        }
    }

    //	The binding object that links us to the DOM node. This might be null
    //	if there is no DOM Node yet (brand new object not attached to a graph)
    protected DOMBinding 		binding;
    
    //	A unique instance for all the nodes of the schema2beans tree
    protected GraphManager 		graphManager;
    
    //
    //	HashMaps of the properties - those are the same properties but are
    //	sorted by Bean name (propByName) and by dtd name (propByDtdName).
    //
    private Map 			propByName;
    private Map 			propByOrder;
    
    //
    //	If we use this hashMap, we have better performances whenever we
    //	access the element using the dtd name. Since schema2beans generate classes
    //	using Bean names, we can assume that very few accesses are performed
    //	using the dtd name and we can then save memory commenting out this 
    //	hashMap. If performance is an issue while accessing the element using
    //	dtd names, we should consider using this hashmap again.
    //
    //private HashMap 			propByDtdName;
    
    //	When an attribute is added, we might not be part of a graph yet.
    //  In this case, we want to cache the attribute information and defer
    //	its processing. Whenever the node is added to a schema2beans tree, 
    //	the values of the cache are automatically check to populate
    //	the attribute values.
    private HashMap			attrCache;
    
    //	We can define an array of comparator, even though most of the usage
    //	is to have one comparator.
    private ArrayList			comparators;

    // This is a provision to mark at runtime the version of each node
    // of the schema2beans tree. As this is not of much use for now, we simply
    // save the memory space.
    //private Version 		version;
    
    //	True if this node is the root of the schema2beans graph.
    private boolean 			isRoot;
    
    //	When the properties are created (calls from the generated class
    //	constructor), we give a number to each property so we know what
    //	their order is. This is how we know, when we add a new property
    //	where it should be added (it has to match the DTD order declaration)
    private int				propertyOrder;

    private String			defaultNamespace;

    /*
    public BaseBean() {
        this(null, new Version(Version.MAJVER, Version.MINVER,
                               Version.PTCVER));
        System.out.println("warning: schema2beans.BaseBean: unknown version of generated beans being used.");
    }
    */
    
    /**
     * @param comps the comparators to use.  Can be null
     * @param version which runtime version to be compatible with.  This should be the version of the generated beans.
     */
    public BaseBean(Vector comps, Version version) {
        init(comps, version);
    }

    protected void init(Vector comps, Version version) {
        if (version.getMajor() < 3) {
            initPropertyTables(13);
        }
        this.comparators = new ArrayList(2);
        //this.version = version;
        this.isRoot = false;
        this.propertyOrder = 0;
        this.attrCache = null;
	
        if ((comps == null) || (comps.size()==0)) {
            //	Use the default comparator
            this.comparators.add(new BeanComparator());
        }
        else {
            int size = comps.size();
            for (int i=0; i the number of expected elements / load factor.
        // If we make the load factor=1, then the initial capacity could be
        // just the expected elements + 1.  However, if 2 elements map to
        // the same bucket, then they start getting strung together in
        // a list, and that slows things down (you lose the O(1) performance).
        // So, double the expected size to make it less likely that 2
        // elements will map to the same bucket.
        //
        int hashTableSize = propertyCount * 2;
        this.propByName = new HashMap(hashTableSize, 1.0f);
        this.propByOrder = new HashMap(hashTableSize, 1.0f);
        //this.propByDtdName = new HashMap(hashTableSize, 1.0f);
    }
    
    //
    //	Dynamically add/remove a comparator. Comparator are used to compare
    //	and merge graphs. There are usually populated in the constructor
    //
    public synchronized void addBeanComparator(BeanComparator cmp) {
	if (cmp != null)
	    this.comparators.add(cmp);
    }
    
    public synchronized void removeBeanComparator(BeanComparator cmp) {
	int i = this.comparators.indexOf(cmp);
	if (i != -1)
	    this.comparators.remove(i);
    }
    
    
    /**
     *	Create a new property for the current bean, selecting default option
     *	values.
     */
    public void createProperty(String dtdName, String beanName, Class type) {
	int o = Common.TYPE_0_1;
	
	if (type.isInstance(java.lang.String.class))
	    o |= Common.TYPE_STRING;
	else
	    o |= Common.TYPE_BEAN;
	
	this.createProperty(dtdName, beanName, o, type);
    }
    
    /**
     *	Create the root element of the graph
     */
    public void createRoot(String dtdName, String beanName,
			   int option, Class type) throws Schema2BeansRuntimeException {
	BeanProp prop = new BeanProp(this, dtdName, beanName,
				     option, type, true);
    try {
        this.graphManager.createRootBinding(this, prop, null);
    } catch (Schema2BeansException e) {
        throw new Schema2BeansRuntimeException(e);
    }
	this.isRoot = true;
    }
    
    /**
     *	Create a new property for the current bean. This creates a BeanProp
     *	object (which is the internal representation of a single/indexed
     *	property) and adds it to the bean property hash table.
     *
     *	This property is later accessed by its bean name.
     */
    public void createProperty(String dtdName, String beanName,
			       int option, Class type) throws
                   Schema2BeansRuntimeException {

	//  This number represents the order of this property amoung
	//  its siblings. The beans generator generates the createProperty()
	//  method calls in the order of the DTD declaration. Therefore, we
	//  can simply relies on the order of the createProperty method calls
	//  to affect the order value for the property.
	this.propertyOrder++;

	BeanProp prop = new BeanProp(this, dtdName, beanName, option, type);
	prop.setOrder(this.propertyOrder);
	Object 	 obj1 = this.propByName.put(beanName, prop);
	//Object 	 obj2 = this.propByDtdName.put(dtdName, prop);
	this.propByOrder.put(String.valueOf(this.propertyOrder), prop);
	
	if (obj1 != null) // || obj2 != null)
	    throw new Schema2BeansRuntimeException(Common.
		getMessage("DuplicateProperties_msg"));
	
	prop.initialize();
    }

    public void setDefaultNamespace(String namespace) {
        defaultNamespace = namespace;
        createAttribute("xmlns", "xmlns", AttrProp.CDATA | AttrProp.IMPLIED,
                        null, namespace);
        setAttributeValue("xmlns", namespace);
    }

    public String getDefaultNamespace() {
        return defaultNamespace;
    }
    
    /**
     *	Returns the list of properties of this bean as an array.
     */
    public BeanProp[] beanProps() {
	int size = this.propByOrder.size();
	BeanProp[] ret = new BeanProp[size];
	for (int i=1; i<=size; i++)
	    ret[i-1] = (BeanProp)this.propByOrder.get(String.valueOf(i));
	return ret;
    }
    
    /**
     *	Returns the list of properties of this bean as an array.
     */
    protected Iterator beanPropsIterator() {
        return propByName.values().iterator();
    }
    
    /**
     *	Return the internal object representation of the property. This method
     *	cannot return null. If there is no object available for the specified
     *	property name, an exception is thrown.
     */
    public BeanProp beanProp(String name) {
	BeanProp prop = (BeanProp)this.propByName.get(name);
	
	if (prop == null) {
	    //	Search using the dtd name
	    String beanName = Common.convertName(name);
	    prop = (BeanProp)this.propByName.get(beanName);
	    
	    if (prop == null)
		throw new IllegalArgumentException(Common.
		    getMessage("BeanPropertyDoesntExist_msg",
			       this.getClass().getName(), name));
	}
	return prop;
    }
    
    /**
     *	Return the internal object representation of the property. This method
     *	cannot return null. If there is no object available for the specified
     *	property name, an exception is thrown.
     */
    public BeanProp beanProp(int order) {
	return (BeanProp)this.propByOrder.get(String.valueOf(order));
    }
    
    /**
     *	Return the value of the single property named name.
     */
    public Object getValue(String name) {
	return this.beanProp(name).getValue(0);
    }
    
    /**
     *	Return one element of the indexed property named name.
     */
    public Object getValue(String name, int index) {
	return this.beanProp(name).getValue(index);
    }
    
    /**
     *	Return one element of the index property using the internal
     *	id index value. This index is unique and doesn't change over
     *	the time of the graph life. This method allows to get an element
     *	of an indexed property without keeping track of index shifting,
     *	and other elements removal.
     */
    public Object getValueById(String name, int id) {
	return this.beanProp(name).getValueById(id);
    }
    
    /**
     *	Convert an unique internal index value into the user visible
     *	index value. The property name specified must be an indexed
     *	property.
     * This method may return -1 if we cannot figure out the index.
     */
    public int idToIndex(String name, int id) {
	return this.beanProp(name).idToIndex(id);
    }
    
    /**
     *	Convert the user index value into the internal unique index value.
     *	The property name specified must be an indexed property.
     */
    public int indexToId(String name, int index) {
	return this.beanProp(name).indexToId(index);
    }
    
    /**
     *	Return true if this property is null
     */
    public boolean isNull(String name) {
	return (this.getValue(name) == null);
    }
    
    /**
     *	Return true if this property is null
     */
    public boolean isNull(String name, int index) {
	return (this.getValue(name, index) == null);
    }
    
    /**
     *	Return the values of the indexed property named name. The result
     *	can be cast as an array of the property type.
     */
    public Object[] getValues(String name) {
	return this.beanProp(name).getValues();
    }
    
    /**
     *	Set the value for the single property named name.
     */
    public void setValue(String name, Object value) {
        setValue(beanProp(name), 0, value);
    }
    
    /**
     *	Set the value of an element for the indexed property named name.
     */
    public void setValue(String name, int index, Object value) {
        setValue(beanProp(name), index, value);
    }

    protected void setValue(BeanProp prop, int index, Object value) {
        prop.setValue(index, value);
    }

    protected int addValue(BeanProp prop, Object value) {
        return prop.addValue(value);
    }

    protected int removeValue(BeanProp prop, Object value) {
        return prop.removeValue(value);
    }
    
    protected void removeValue(BeanProp prop, int index) {
        prop.removeValue(index);
    }
    
    /**
     *	Set the value of an element for the indexed property named name,
     *	using the unique internal index.
     */
    public void setValueById(String name, int id, Object value) {
	BeanProp bp = this.beanProp(name);
	int index = bp.idToIndex(id);
	bp.setValue(index, value);
    }
    
    /**
     *	Set the values for the indexed property named name.
     */
    public void setValue(String name, Object[] value) {
	this.beanProp(name).setValue(value);
    }
    
    /**
     *	Add a value to the indexed property named name.
     */
    public int addValue(String name, Object value) {
        return addValue(beanProp(name), value);
    }
    
    /**
     *	Remove a value from the indexed property named name.
     */
    public int removeValue(String name, Object value) {
        return removeValue(beanProp(name), value);
    }

    /**
     *	Remove a value from the indexed property named name.
     */
    public void removeValue(String name, int index) {
        removeValue(beanProp(name), index);
    }
    
    /**
     *	Returns the position of the indexed property element.
     *	If the type of the property is a bean, use the == comparison,
     *	else use the equals() method.
     *	If the element is not found, return -1.
     */
    public int indexOf(String name, Object value) throws
    Schema2BeansRuntimeException {
	BeanProp 	bp = this.beanProp(name);
	
	if (bp == null)
	    throw new Schema2BeansRuntimeException(Common.
		getMessage("UnknownPropertyName_msg", name));
	
	if (Common.isArray(bp.type)) {
	    boolean isBean = Common.isBean(bp.type);
	    
	    int size = bp.size();
	    for (int i=0; i0) {
	    Iterator it = this.attrCache.keySet().iterator();
	    int i = 0;
	    while (it.hasNext())
		ret[i++] = it.next().toString();
	}
	
	return ret;
    }
    
    /**
     *	Return the value cached for the attribute named name.
     */
    String cachedAttributeValue(String name) {
	if (this.attrCache != null)
	    return (String)this.attrCache.get(name);
	else
	    return null;
    }
    
    /**
     *	Return the value cached for the attribute named name.
     */
    void cachedAttributeClear() {
	this.attrCache = null;
    }
    
    /**
     *	Get the attribute value on the current property bean.
     *  If there is no current attribute (or element for that matter),
     *  then null is returned.
     */
    public String getAttributeValue(String name) {
	BeanProp bp = this.beanProp();
	if (bp != null) {
	    //	Find out what our index is within the BeanProp object
	    int i = bp.idToIndex(this.binding.getId());
        if (i < 0)   // I guess we're not part of the BeanProp yet.
            return null;
	    return bp.getAttributeValue(i, name);
	}
	else {
	    //
	    //	That's a brand new bean not attached yet to a graph. Try
	    //	to get the value from the cache.
	    //
	    if (this.attrCache != null)
		return (String)this.attrCache.get(name);
	    else
		return null;
	}
    }
    
    /**
     *	Get the attribute value (see BeanProp class)
     */
    public String getAttributeValue(String propName, String name) {
	return this.beanProp(propName).getAttributeValue(0, name);
    }
    
    /**
     *	Set the value of the attribute (see the BeanClass class)
     */
    public void setAttributeValue(String propName, int index, String name,
				  String value) {
	this.beanProp(propName).setAttributeValue(index, name, value);
    }
    
    /**
     *	Get the attribute value (see BeanProp class)
     */
    public String getAttributeValue(String propName, int index, String name) {
	return this.beanProp(propName).getAttributeValue(index, name);
    }
    
    /**
     *	Return the list of all known attribute names for this property
     *	(even if they are not set).
     */
    public String[] getAttributeNames(String propName) {
	return this.beanProp(propName).getAttributeNames();
    }
    
    /**
     *	Return the list of all known attribute names for the current bean
     */
    public String[] getAttributeNames() {
	BeanProp bp = this.beanProp();
	if (bp != null)
	    return bp.getAttributeNames();
	else
	    return null;
    }
    
    
    /**
     *	Return the list of all known attribute names for this property
     *	(even if they are not set).
     */
    public BaseAttribute[] listAttributes(String propName) {
	return this.beanProp(propName).getAttributes();
    }
    
    /**
     *	Return the list of all known attribute names for the current bean
     */
    public BaseAttribute[] listAttributes() {
	BeanProp bp = this.beanProp();
	if (bp != null)
	    return bp.getAttributes();
	else
	    return null;
    }
    
    
    //	Called by find() method. Where the attributes are searched for.
    private void lookForAttribute(ArrayList found, BeanProp bp, BaseBean bean,
				  BaseAttribute[] attrs, String attrName,
				  Object value) {

	for (int j=0; j":bean.getClass().getName()) +
			    " - " + propName + "/" + attrName + " for value " +
			    ((value == null)?"":value.toString()));
	}
	
	if (bean == null || value == null)
	    return;
	
	BaseProperty[] props = bean.listProperties();
	
	//
	//  Search our own attributes first (as any node might be the
	//  root of the search, we have to start by ourself first)
	//
	BaseAttribute[] attrs = bean.listAttributes();
	if (propName == null && attrs != null && attrs.length > 0) {
	    BeanProp bp = bean.beanProp();
	    this.lookForAttribute(found, bp, bean, attrs, attrName, value);
	}
	
	//
	//  Look for the properties and the attributes of the non-bean
	//  properties (bean property attributes are searched as the root
	//  case explained above)
	//
	for (int i=0; i 0)
		    this.lookForAttribute(found, bp, null, attrs,
					  attrName, value);
	    }
	    
	    // recurse
	    if (p.isBean() && p.isIndexed()) {
		BaseBean[] ba = (BaseBean[])bean.getValues(name);
		for (int k=0; k G1 + E2
     *		INTERSECT is	G1 n G2 <=> (G1 U G2) - E1 - E2
     */
    public static final int MERGE_NONE 		= 0x00;
    public static final int MERGE_INTERSECT	= 0x01;
    public static final int MERGE_UNION		= 0x02;
    public static final int MERGE_UPDATE 	= (MERGE_UNION|MERGE_INTERSECT);
    public static final int MERGE_COMPARE 	= 0x04;
    
    
    static String mergeModeToString(int mode) {
	switch(mode) {
	    case MERGE_NONE: return "MERGE_NONE";		// NOI18N
	    case MERGE_INTERSECT: return "MERGE_INTERSECT";	// NOI18N
	    case MERGE_UNION: return "MERGE_UNION";		// NOI18N
	    case MERGE_UPDATE: return "MERGE_UPDATE";	// NOI18N
	    case MERGE_COMPARE: return "MERGE_COMPARE";	// NOI18N
	    default: return "Unknown merge mode: " + mode;	// NOI18N
	}
    }

    /**
     *	Merge the specified bean schema2beans graph into the current graph using
     *	the specified mode.
     */
    public void merge(BaseBean bean, int mode) {
        if (mode == MERGE_UPDATE)
            mergeUpdate(bean);
        else
            mergeTreeRoot(bean, mode);
    }
    
    /**
     *	Merge the bean tree with the current graph using the default 
     *	merging option.
     */
    public void merge(BaseBean bean) {
        mergeUpdate(bean);
    }

    /**
     * Same as merge(BaseBean bean).
     * It's possible to override this method and make it more efficient,
     * than the generic one.
     */
    public void mergeUpdate(BaseBean sourceBean) {
        mergeTreeRoot(sourceBean, MERGE_UPDATE);
    }
    
    //	Called by mergeTree to set the default hasKey value
    private boolean setHasKeyDefaultValue(BeanProp prop) {
	BeanComparator cmp = (BeanComparator)this.comparators.get(0);
	return cmp.hasKeyDefined(prop);
    }

    /*
     *	Copy a property from the graph 'bean' to the graph 'prop', making
     *	sure that both property and attributes are copied.
     *	This method is used to copy non BaseBean properties
     *	(a clone on a BaseBean automatically copies the attributes).
     */
    protected void copyProperty(BeanProp prop, BaseBean bean, 
                                int index, Object value) {

        boolean isArray = Common.isArray(prop.type);
        String name = prop.getName();

        //  Copy the property value
        if (value == null) {
            if (isArray)
                value = bean.getValue(name, index);
            else
                value = bean.getValue(name, 0);
        }

        int newIndex = 0;

        if (isArray) {
            newIndex = addValue(prop, value);
        } else {
            setValue(prop, 0, value);
            index = 0;
        }

        this.copyAttributes(prop, newIndex, bean, index);
    }

    /*
     *	This copies the attributes of a property from 'bean' (whole BaseBean)
     *	to a property BeanProp (the specific BeanProp of the other BaseBean
     *	where the copy as to occur).
     */
    private void copyAttributes(BeanProp prop, int propIndex, 
			        BaseBean bean, int beanIndex) {

	//	Copy the attributes
	String name = prop.getName();
	BaseAttribute[] ba = bean.listAttributes(name);
	if (ba != null) {
	    for(int j=0; j":bean.getClass().getName()) +
			    " - " + mergeModeToString(mode));
	}
	
	//
	//	The merge method is called only when two beans are logically
	//	identical. Therefore, this method doesn't try to check if it
	//	is equal to the other beans, but find out which properties
	//	have to be updated.
	//
	if (this.getClass().isInstance(bean)) {
	    //	We got the same as ourself in another graph
	    Iterator it = beanPropsIterator();
	    
	    //
	    //	Parse our attributes
	    //
	    //	To have the following code easier to read, we could
	    //	call the setter/getter method of the BaseBean object for
	    //	both our attributes and the bean-to-merge attributes.
	    //	However, since we get the BeanProp objects from our
	    //	properties hashtable, we can call directly the BeanProp
	    //	getter/setter methods for our properties and the
	    //	BaseBean getter/setter for the bean we have to merge.
	    //
	    while (it.hasNext()) {
		//	Get our next property (as a BeanProp)
		BeanProp prop = (BeanProp)it.next();
		
		if (prop == null)
		    continue;
		
		String 		name = prop.getBeanName();
		boolean 	isArray = Common.isArray(prop.type);
		boolean 	isBean = Common.isBean(prop.type);
		Object 		o1, o2, o3;
		boolean		hasKey = false;
		boolean		hasKeyDefined = false;
		
		if (isArray) {
		    //
		    //	For each element of the index property, we have to
		    //	find if there is a matching element in the other
		    //	indexed property. If there is, merge the two
		    //	elements if this is a bean. If there are no
		    //	matching elements, remove it. At the end,
		    //	add any new elements of the other indexed property.
		    //
		    int		i, j = 0;
		    int 	size1 = prop.size();
		    int 	size2 = bean.size(name);
		    boolean 	toRemove[] = new boolean[size1];
		    boolean 	toAdd[] = new boolean[size2];
		    boolean 	compared[] = new boolean[size2];
		    
		    //	To keep track of that need to be removed
		    Arrays.fill(toRemove, false);
		    
		    //	To keep track of what we'll need to add after the loop
		    Arrays.fill(toAdd, true);
		    
		    //	To make sure that we do not match twice the same elt
		    Arrays.fill(compared, false);
		    
		    if (DDLogFlags.debug) {
			TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
					DDLogFlags.DBG_UBN, 5,
					DDLogFlags.MERGEPROP,
					this.getClass().getName() + "." +
					name + "[" + size1 + "] / " +
					bean.getClass().getName() + "." +
					name + "[" + size2 + "]");
		    }
		    
		    //	For each of our current property elts ...
		    for (i=0; i " +
                                        name + "[" + j + "]");
                    }
                } else {
                    toRemove[i] = true;	//	no more exists
			    
                    if (DDLogFlags.debug) {
                        TraceLogger.put(TraceLogger.DEBUG,
                                        TraceLogger.SVC_DD,
                                        DDLogFlags.DBG_UBN, 5,
                                        DDLogFlags.MERGENTFND,
                                        name + "[" + i +
                                        "] to be removed");
                    }
                }
		    }
		    
		    //
		    //	We want to make sure that we set a proper value
		    //	to hasKey when one of the two array is empty
		    //	(either null or containing null elements)
		    //
		    if (!hasKeyDefined)
                hasKey = this.setHasKeyDefaultValue(prop);
		    
		    if ((mode & MERGE_COMPARE) == MERGE_COMPARE) {
                //	Any diff returns false
                for (i=0; i Same");
                        continue;
                    }
                    //System.out.println("--> Different");
                    if (pos1 >= size1) {
                        //System.out.println("New stuff added to end.");
                        for (; pos2 < size2; ++pos2) {
                            n2 = nodes2.item(pos2);
                            if (n2 instanceof CharacterData) {
                                Node newNode = doc1.importNode(n2, true);
                                startingNode1.appendChild(newNode);
                            }
                        }
                        break;
                    }
                    if (pos2 >= size2) {
                        //System.out.println("Stuff deleted from end.");
                        for (int i = size1 - 1; i >= pos1; --i) {
                            n1 = nodes1.item(i);
                            if (n1 instanceof CharacterData) {
                                startingNode1.removeChild(n1);
                            }
                        }
                        break;
                    }
                    if (n1 instanceof CharacterData &&
                        n2 != null && n2.getNodeType() == n1.getNodeType()) {
                        String value1 = n1.getNodeValue();
                        String value2 = n2.getNodeValue();
                        // Are they both just whitespace?
                        if ((value1 == null || "".equals(value1.trim())) &&
                            (value2 == null || "".equals(value2.trim())) ) {
                            //System.out.println("whitespace was changed in graph 2");
                            ((CharacterData)n1).setData(value2);
                            ++pos1;
                            ++pos2;
                            continue;
                        }
                    }
                    int j2 = pos2;
                    int j1 = -1;
                    for (; j2 < size2; ++j2) {
                        Node j2node = nodes2.item(j2);
                        // Skip whitespace here
                        if (j2node instanceof CharacterData) {
                            String j2value = j2node.getNodeValue();
                            if (j2value == null || "".equals(j2value.trim()))
                                continue;
                        }
                        j1 = findInNodeList(nodes1, j2node, pos1);
                        if (j1 >= 0)
                            break;
                    }
                    //System.out.println("j1="+j1+" j2="+j2);
                    if (j1 == pos1 && j2 > pos2) {
                        //System.out.println("stuff was added in graph 2");
                        // pos2 thru j2 were added (including pos2 and not j2)
                        Node nodeToInsertBefore = nodes1.item(j1);
                        for (; pos2 < j2; ++pos2) {
                            n2 = nodes2.item(pos2);
                            if (n2 instanceof CharacterData) {
                                Node newNode = doc1.importNode(n2, true);
                                //System.out.println("newNode="+newNode);
                                ++pos1;
                                startingNode1.insertBefore(newNode, nodeToInsertBefore);
                            }
                        }
                        // reestablish nodes1 and size1 data
                        nodes1 = startingNode1.getChildNodes();
                        size1 = nodes1.getLength();
                    } else if (j1 - 1 < pos1) {
                        //System.out.println("Simply replace n1 with n2");
                        if (n2 instanceof CharacterData) {
                            startingNode1.replaceChild(doc1.importNode(n2, true), n1);
                        }
                        ++pos1;
                        ++pos2;
                    } else {
                        //System.out.println("stuff was deleted in graph 2");
                        for (int i = j1 - 1; i >= pos1; --i) {
                            n1 = nodes1.item(i);
                            if (n1 instanceof CharacterData) {
                                //System.out.println("Deleting: "+n1);
                                startingNode1.removeChild(n1);
                            } else {
                                ++pos1;
                            }
                        }
                        // reestablish nodes1 and size1 data
                        nodes1 = startingNode1.getChildNodes();
                        size1 = nodes1.getLength();
                    }
                }
            }
        }
	    
	    //
	    //	For the MERGE_COMPARE option: if we reach this point, that
	    //	means we didn't find any diff. We can therefore return true.
	    //	Any other option returns always true.
	    //
	    return true;
	}
	else
	    throw new IllegalArgumentException(Common.getMessage(
	        "MergeWrongClassType_msg", this.getClass().getName(),
		(bean==null ? "" : bean.getClass().getName())));
    }

    /**
     * Compare 2 Node's and tell me if they're roughly equivalent.
     * By roughly equivalent, attributes and children are ignored.
     */
    private boolean areNodesEqual(Node node1, Node node2) {
        if (node1 == null && node2 == null)
            return true;
        if (node1 == null || node2 == null)
            return false;
        if (node1.getNodeType() != node2.getNodeType())
            return false;
        if (!node1.getNodeName().equals(node2.getNodeName()))
            return false;
        String value1 = node1.getNodeValue();
        String value2 = node2.getNodeValue();
        if (value1 == null) {
            if (value2 != null)
                return false;
        } else if (!value1.equals(value2))
            return false;
        return true;
    }

    /**
     * Search in @param nodes for an equivalent node to @param node
     * (equivalent as defined by areNodesEqual) starting the search
     * at position @param start.
     */
    private int findInNodeList(NodeList nodes, Node node, int start) {
        return findInNodeList(nodes, node, start, nodes.getLength());
    }

    /**
     * Search in @param nodes for an equivalent node to @param node
     * (equivalent as defined by areNodesEqual) starting the search
     * at position @param start, giving up the search at position
     * @param maxPosition.
     */
    private int findInNodeList(NodeList nodes, Node node,
                               int start, int maxPosition) {
        for (; start < maxPosition; ++start) {
            if (areNodesEqual(nodes.item(start), node))
                return start;
        }
        return -1;
    }

    /**
     *	Perform a deep recursive comparison. Return true if the two graphs
     *	are equals, false otherwise.
     *
     *	The comparison is using the comparators and therfore returns a
     *	result wrt to the comparators. That means a true return value
     *	only means that the graphs are logically equals depending on the
     *	comparator implementation.
     *
     *	Note that the default comparator compares every property value.
     *
     */
    public boolean equals(Object obj) {
	boolean ret = false;
	
	try {
	    if (this == obj)
		return true;
	    else
		if (obj instanceof BaseBean)
		    ret = this.mergeTreeRoot((BaseBean)obj, MERGE_COMPARE);
	}
	catch(Exception e) {
	    //	Equals method only returns either true or false
	    if (DDLogFlags.debug) {
		TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
				DDLogFlags.DBG_UBN, 1, DDLogFlags.EQUALS,
				"got exception while comparing: " +
				e + "\n");
		e.printStackTrace();
	    }
	    ret = false;
	}
	
	if (DDLogFlags.debug) {
	    TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
			    DDLogFlags.DBG_UBN, 1, DDLogFlags.EQUALS,
			    (ret?"true":"false"));
	}
	return ret;
    }

    public int hashCode() {
        int result = 17;
        for (Iterator it = beanPropsIterator(); it.hasNext(); ) {
            BeanProp prop = (BeanProp) it.next();
            boolean 	isArray = Common.isArray(prop.type);
            int size;
            if (isArray) {
                size = prop.size();
            } else {
                size = 1;
            }
            for (int propNum = 0; propNum < size; ++propNum) {
                Object obj = prop.getValue(propNum);
                result = 37*result + (obj == null ? 0 : obj.hashCode());
            }
        }
        // And attributes
        if (beanProp() != null) {
            String[] attributeNames = beanProp().getAttributeNames();
            for (int attrNum = 0; attrNum < attributeNames.length; ++attrNum) {
                String attrName = attributeNames[attrNum];
                String attrValue = getAttributeValue(attrName);
                result = 37*result + (attrValue == null ? 0 : attrValue.hashCode());
            }
        }
        return result;
    }
    
    /**
     *	Get the bean using its unique identifier. This identifier is not
     *	the indexed position of the element in the array but a unique id
     *	associated to the DOMBinding object (see the BeanProp and DOMBinding
     *	classes)
     */
    public Bean propertyById(String name, int id) {
        BeanProp bp = this.beanProp(name);
	
        if (Common.isBean(bp.type)) {
            if (Common.isArray(bp.type))
                return (BaseBean)bp.getValueById(id);
            else
                return (BaseBean)bp.getValue(0);
        }
        else
            throw new IllegalStateException(Common.
                                            getMessage("PropertyIsNotABean_msg", name));
    }
    
    /**
     *	Return the BeanProp object where this bean belongs.
     */
    public BeanProp beanProp() {
	if (this.binding != null)
	    return this.binding.getBeanProp(this);
	else
	    return null;
    }

    /**
     *  Return the BaseBean parent of the current bean. This might return null
     *	either because this is the root of the graph or because this node is not
     *	part of a schema2beans graph yet.
     */
    public BaseBean parent() {
        BeanProp bp = this.beanProp();
	
        if (bp != null)
            return this.beanProp().getBean();
        else
            return null;
    }

    public Bean _getParent() {
        return parent();
    }

    /**
     * Return the root of the graph.  If the graph is not connected to a
     * generated root, then the topmost bean is returned.
     */
    public Bean _getRoot() {
        Bean b = this;
        Bean bParent;
        while (!b.isRoot()) {
            bParent = b._getParent();
            if (bParent == null)
                break;
            b = bParent;
        }
        return b;
    }

    /**
     *	Return the path name of this element in the graph.
     */
    public String fullName() {
	StringBuffer str = new StringBuffer();
	this.buildPathName(str);
	return str.toString();
    }
    
    public boolean hasName(String name) {
	if (name != null)
	    return (name.equals(this.name()) || name.equals(this.dtdName()));
	else
	    return false;
    }

    /**
     *	Return true if this element is the root of the schema2beans tree.
     */
    public boolean isRoot() {
	return this.isRoot;
    }

    /**
     *	Return the bean name of this schema2beans graph node.
     */
    public String name() {
	BeanProp bp = this.beanProp();
	
	if (bp != null)
	    return this.beanProp().getBeanName();
	else
	    return "";	// NOI18N
    }
    
    /**
     *	Return the DTD name of this schema2beans graph node.
     */
    public String dtdName() {
	return this.beanProp().getDtdName();
    }
    
    public void createBean(Node node, GraphManager mgr) throws Schema2BeansRuntimeException {
	if (this.isRoot) {
	    mgr.completeRootBinding(this, node);
	}
	
	this.graphManager = mgr;
    try {
        mgr.fillProperties(this.beanProps(), node);
    } catch (Schema2BeansException e) {
        throw new Schema2BeansRuntimeException(e);
    }
    }
    
    /**
     *	Return a new instance of the specified bean property
     */
    public BaseBean newInstance(String name) {
	return this.beanProp(name).newBeanInstance();
    }
    
    
    public abstract void dump(StringBuffer str, String indent);

    // The old abstract verify method never did anything.
    // a validate method will get generated if the -validate option is on.
    //public abstract void validate() throws org.netbeans.modules.schema2beans.ValidateException;
    
    /**
     *	Dump the DOM content of this bean. If nodeName is specified,
     *	dump only from the subname named nodeName.
     */
    public String dumpDomNode(String nodeName, int depth) {
	Node n = null;
	
	if (this.binding == null)
	    return "";	// NOI18N
	else {
	    n = this.binding.node;
	    if (n == null)
		return "";	// NOI18N
	}
	
	return DDFactory.XmlToString(n, depth, nodeName);
    }
    
    public String dumpDomNode(int depth) {
	return this.dumpDomNode(null, depth);
    }
    
    public String dumpDomNode() {
	return this.dumpDomNode(null, 99999);
    }
    
    public String dumpBeanNode() {
	return null;
    }
    
    public void dumpAttributes(String name, int index, StringBuffer str,
    String indent) {
	String[] names = this.getAttributeNames(name);
	
	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.