All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tangosol.dev.component.Property Maven / Gradle / Ivy

There is a newer version: 24.09
Show newest version
/*
 * Copyright (c) 2000, 2021, Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * http://oss.oracle.com/licenses/upl.
 */

package com.tangosol.dev.component;


import com.tangosol.dev.assembler.Field;
import com.tangosol.dev.assembler.Constant;
import com.tangosol.dev.assembler.IntConstant;
import com.tangosol.dev.assembler.LongConstant;
import com.tangosol.dev.assembler.FloatConstant;
import com.tangosol.dev.assembler.DoubleConstant;
import com.tangosol.dev.assembler.StringConstant;

import com.tangosol.util.ClassHelper;
import com.tangosol.util.ErrorList;
import com.tangosol.util.LiteSet;
import com.tangosol.util.SimpleEnumerator;
import com.tangosol.util.NullImplementation;
import com.tangosol.util.Binary;
import com.tangosol.run.xml.XmlElement;

import java.beans.PropertyVetoException;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.io.PrintWriter;

import java.lang.reflect.Modifier;

import java.text.Collator;

import java.util.Enumeration;
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;


/**
* A Property implements the set of information that describes a component's
* state declaration and definition.  Properties provide an interface to a
* component's state and are also used to provide named constant values.
*
* Properties are implemented using Java methods following the Javabeans
* naming conventions.  If storage is required for the property's value, a
* Java field is created in the Java class that results from the Component
* Definition that declares the Property.  Properties can be indexed, which
* means that the property represents an array of values that can be accessed
* as an array or individually.  For a property of type T with name N, the
* following Java methods (called property accessors) are created:
*
* Direction             PROP_SINGLE     PROP_INDEXED        PROP_INDEXEDONLY
* --------------------  --------------  ------------------  -----------------
* in (settable)         void setN(T)    void setN(T[])
*                                       void setN(int, T)   void setN(int, T)
*
* out and T!=boolean    T getN()        T[] getN()
*    (gettable)                         T getN(int)         T getN(int)
*
* out and T==boolean    T isN()         T[] isN()
*    (gettable)                         T isN(int)          T isN(int)
*
* Properties can originate from a super component, from integration, or can
* be manually added.  (Properties cannot be created simply by adding the
* appropriately named Behaviors.  Each Property represents a consious
* decision to add state information to the Component Definition.)
*
* Regarding Property declaration, certain attributes are designed to provide
* tools the needed information to:
*   - Create the Property, for example using a wizard-like process
*   - Provide scripting assistance in a script editor
*   - Generate a Java field, if necessary, to store the property value, and
*     to provide exact field modifiers (public/private/protected, static,
*     final, transient; volatile not used)
*   - Generate the desired Java methods (Property accessors) to manage the
*     property, providing default method implementations if necessary
*   - Generate documentation, similar to JavaDoc
*   - Generate BeanInfo classes
*   - Generate CORBA IDL for the property
*
* Regarding the Property definition, there are four categories to consider:
*   1.  The visual design of Property values, for example, in a property
*       sheet
*   2.  The availability of a Property to a scripting language, i.e. getting
*       and/or setting Property values
*   3.  The requirement that a Property's value be stored persistently as
*       part of a Component Definition
*   4.  The Java fields, methods, and code generated by the Component
*       compilation process, specifically:
*       1.  Java fields which provide runtime storage for Property values,
*           including (for Java intrinsic and String constants) the
*           "ConstantValue" attribute information
*       2.  Java class initialization (the unnamed static method which is
*           compiled as the "static " method) which initializes
*           constants that cannot be initialized using the "ConstantValue"
*           attribute (delegating to __initStatic)
*       3.  Java methods generated to implement virtual constants
*       4.  Java methods (__init and __initPrivate) generated to implement
*           Component initialization (via Property setters)
*       5.  Java methods generated as the default implementation of various
*           accessors in the absence of implementation by the developer
*           1.  For properties that are neither constant nor private (which
*               implies that accessors must exist)
*           2.  For indexed properties that have only one of the accessors
*               (indexed or single) implemented
*
* Properties fall into one of the following categories:
*   - Java constants - a constant field is generated in the Java class that
*     results from the Component Definition that declares the constant.  The
*     Property is declared as "persistent final out" and the resulting Java
*     field is "final".
*   - Virtual constants - a Property which is implemented by a compiler-
*     generated accessor based on a design-time value.  The property is
*     declared as "persistent instance derivable out" and no Java field
*     results.  No accessor Behavior(s) corresponding to the virtual
*     constant are declared, but they are reserved.  (See the note below
*     regarding the Accessible attribute.)
*   - Calculated Properties - a Property which is declared as "transient out"
*     is considered to be a calculated Property, which means no corresponding
*     Java field is generated.  A calculated Property is implemented by the
*     developer.  Consider as an example the calculated Property NCount which
*     returns the count of elements for the indexed Property N such that the
*     index to the accessor "T getN(int)" is in the range 0 to NCount-1.
*     Calculated Properties are not settable (they are declared as "out"),
*     they do not appear in a property sheet at design time, they do not have
*     persisted values in the Component Definition, and they are not
*     initialized (__init, __initPrivate, __initStatic).
*   - Functional Properties - a Property which is settable-only (declared as
*     "in") is considered to be a functional Property.  A functional Property
*     is implemented by the developer and has no corresponding Java field.
*     Consider as an example the functional Property AlwaysFinalize of the
*     Application Component, which allows the developer to ensure the
*     finalizers will be executed in the application simply by providing a
*     value of true in the property sheet of the application being designed;
*     the implementation of the AlwaysFinalize setter simply delegates to the
*     java.lang.Runtime method runFinalizersOnExit.  Functional Properties
*     are not gettable (they are declared as "in"), but they do appear in the
*     property sheet at design time, they are persisted in the Component
*     Definition, and they are initialized (which invokes the setter).
*   - Standard Properties - All Properties which are not in one of the above
*     categories are considered to be standard Properties, and are declared
*     as "inout".  A Java field is generated for standard Properties if
*     storage is required.  Java methods are auto-generated for non-private
*     standard Properties if they are declared but not implemented; the value
*     of a private standard Property can be accessed using a get or set field
*     operation if the corresponding accessor is not implemented.
*
* The attributes of a Property are:
*
*   - Origin - a Property has one of three origins:
*     - Super      - declared at a super level
*     - Integrates - declared by integration at this level
*     - Manual     - declared manually at this level
*
*   - Exists - a Property is an EXISTS_INSERT at its declaration level and an
*     EXISTS_UPDATE at subsequent levels.  When a Property modification or
*     derivation is extracted and no changes exist (i.e. null derivation),
*     the property derivation/modification can be discarded.
*
*   - Access - this attribute only applies to constant Properties.  If
*     the Property is a Java constant, this attribute specifies the
*     accessibility of the generated final Java field.  If the Property is
*     a virtual constant, this attribute specifies the accessibility of the
*     generated Java method.
*
*   - Static - a static Property has a value that is shared by all instances
*     of the Component.  The accessor Behaviors for a static Property are
*     also static, which implies non-virtual (no "this").
*
*   - Final - the final attribute is used to differetiate Java constants
*     (persistent final out) from virtual constants (persistent instance
*     derivable out).
*
*   - Visible - The Visibility attribute is used only for design and
*     compilation purposes.  For example:
*     - A Property that is neither VIS_VISIBLE nor VIS_ADVANCED will not
*       show up in a generated BeanInfo
*     - A Property that is neither VIS_VISIBLE nor VIS_ADVANCED will not
*       be created in the resulting visual Javabean (i.e. "multi-column
*       integration)".
*     - A Property that is not VIS_VISIBLE will not be displayed in the
*       property sheet by default.  The developer must choose to see other
*       levels of visibility (VIS_ADVANCED, VIS_HIDDEN, VIS_SYSTEM).
*
*   - Deprecated - The Deprecated attribute is used for design and
*     compilation purposes.  Use of a Property which is deprecated can
*     produce a deprecation warning.
*
*   - Persistent - If a Property results in the generation of a Java field,
*     the persistent attribute determines whether the field is marked as
*     transient or not.  If a Property is gettable-only (declared as "out"),
*     persistent means constant and transient means calculated; see the
*     categories "Java constants", "virtual constants", and "calculated
*     Properties" above.
*
*   - Direction - a Property is gettable and/or settable:
*     - DIR_IN    - settable only
*     - DIR_OUT   - gettable only
*     - DIR_INOUT - gettable/settable
*
*   - DataType - A Property has a constraint on the type of value it can
*     hold; this is the data type of the Property (referred to as "T").  A
*     Property can have one of the following types:
*     - Intrinsic Java type (plus types which are "almost" intrinsic)
*       - boolean
*       - byte
*       - char
*       - short
*       - int
*       - long
*       - float
*       - double
*       - binary (byte[])
*       - text (java.lang.String)
*     - Complex - the Component Definition is used to implement complex
*       (i.e. hierarchical) Properties.  For example, the Font Property of
*       a visual control (Component.GUI.Control) is of DataType Component
*       with the constraint Component.GUI.Font, meaning that the Component
*       selected for the Font Property must derive from (inclusive of)
*       Component.GUI.Font.  (The constraint could also be a Java interface
*       name.)  Once a Component is selected which fulfills the constraint,
*       the selected Component's Properties displayed hierarchically under
*       it in the property sheet, allowing them to be individually set.
*       (Persistence, scriptability, and code generation are all covered
*       by this design; see below.)
*     - Serializable - Any Java class which implements or Java interface
*       which extends the Java interface java.io.Serializable is a suitable
*       DataType for a Property.  (Use of Serializable is strongly
*       discouraged due to lack of derivation capability and problems with
*       versioning.)  At design time, Serializable objects are boiled down
*       to binary by serializing the object into a byte array stream.  At
*       class initialization, the binary data is deserialized.
*     - Other Java type, class, or interface - A Property can have any
*       legal Java type, but if the type is not Intrinsic, Component, or
*       Serializable, then the value is not designable.  (See Value below.)
*
*   - Indexed - This attribute corresponds directly to Javabean indexed
*     properties as defined in the Java Beans specification (7.2):
*     - PROP_SINGLE      - the property represents a value of type DataType
*     - PROP_INDEXED     - the property represents an array of values, each
*       of type DataType, that can be accessed as individual elements or as
*       an array object
*     - PROP_INDEXEDONLY - the property represents an array of values, each
*       of type DataType, that can only be accessed as individual elements;
*       a Java constant property cannot be indexed-only because it is backed
*       by a single Java field (i.e. field access is an "array accessor");
*       standard properties that are indexed-only do not generate a Java
*       field for the same reason
*
*   - Name - a Property is uniquely identified by its name (referred to as
*     "N").  The name is also used as the basis for the names of the related
*     accessor Behaviors.
*
*   - Value - the Property value reflects the Property data type.
*
*     - Intrinsic - the value is an object that corresponds directly to the
*       data type:
*
*       - java.lang.Boolean         - boolean
*       - java.lang.Byte            - byte
*       - java.lang.Character       - char
*       - java.lang.Short           - short
*       - java.lang.Integer         - int
*       - java.lang.Long            - long
*       - java.lang.Float           - float
*       - java.lang.Double          - double
*       - byte[] or null            - binary
*       - java.lang.String or null  - text
*
*       In the future, additional types may be added to the intrinsic
*       category.  For example, the above list contains binary, which can
*       be considered an array of byte; support for arrays (as opposed to
*       indexed Properties) of other types could be added relatively easily.
*
*     - Complex - the value of the Property is an instance of a Component
*       Definition containing only Identity and State information.  See
*       below.  (Since complex values are based on Component Definitions,
*       which represent a reference type, the value can be null.)
*
*     - Serializable - the Property value is a Java object that implements
*       the interface java.io.Serializable.  When the Property persists,
*       the object is serialized and stored as a byte array.  (Since
*       java.io.Serializable represents a reference type, the value can be
*       null.)
*
*     - Other - since the DataType of the Property can be any legal Java
*       type, and all intrinsic types are covered (above), this category
*       is the catch-all for non-complex, non-Serializable reference types.
*       The only designable values is null because there is no way to persist
*       or initialize any other value.
*
*     Additionally, any Property value can have the special value "No Value",
*     which means that no value is present for the Property; this implies,
*     for example, that code will not be generated to initialize the Property
*     during instantiation.  Important!!!  The value "No Value" is not the
*     same as null; null is a very specific value refering to Java's null
*     reference data type (see The Java Language Reference, 4.1, 4.5.2).
*
*     For non-indexed Properties, the Value attribute is managed using the
*     getValue() (returning java.lang.Object) and setValue(java.lang.Object)
*     accessors.  Indexed Properties are slightly more complex to manage due
*     to the design-time capabilities of:
*
*       - changing specific (indexed) values
*       - adding values (both "inserting" and "appending")
*       - removing values
*       - re-ordering values
*
*     To support indexed Properties, the Property class adds additional
*     methods to accomplish each of the above.  Specifically, an indexed
*     getter and setter are added, as well as an accessor for the index
*     extent (number of elements).  For an indexed Property, the single
*     getter returns an array of java.lang.Object and the single setter
*     takes an array of java.lang.Object, allowing the caller to modify,
*     add, remove, and re-order the values.  Additionally, methods are
*     provided to add and remove elements directly without having to
*     get, manipulate, and set the array.
*
*     For .class generation, the initialization code for indexed Properties
*     will use the array (single) setter if one is available (i.e. if the
*     Property is not PROP_INDEXONLY).  At this point, "No Value" is
*     replaced with the default value for the type (false for boolean,
*     0 for numeric, and null for reference types).  If the Property is
*     declared as PROP_INDEXONLY, the initialization code will set the
*     specified values (i.e. all indexed values that are not "No Value")
*     by calling the indexed setter for each.
*
* Complex Properties are implemented using Component Definitions.
*
*   1.  Visual Design - The specified Component Definition (which is subject
*       to a Component derivation or interface implementation constraint) is
*       instantiated, but only its State and Identity traits are relevant.
*       The property sheet tool supports user selection of the Component
*       Definition (subject to the constraint) and allows the Properties of
*       the Component Definition to be set.  The Properties may themselves
*       be complex, thus the Property may be recursively complex.
*
*   2.  Scripting - One of the primary reasons that complex Properties are
*       necessary is the inability of most "raw" Javabeans to support multi-
*       level Properties.  Consider the example of the Text control (of type
*       Component.GUI.Control.Edit):
*
*           Text.Font.Size = 24
*
*       The desired result is that the text in the edit control change its
*       point size to 24.  The required implementation using the "raw" awt
*       classes would be:
*
*           Text.setFont(new java.awt.Font(Text.getFont().getName(),
*                   Text.getFont().getStyle(), 24));
*
*       In order to write the above, it requires significant knowledge of
*       the java.awt.Font class, including its constructor, the fact that
*       a Font is immutable, and the mapping of available read-only accessors
*       to the required contructor parameters.  It is a theoretically
*       unsolvable problem to generate the desired code for each (known and
*       also as yet unknown) supporting class such as java.awt.Font.  (For
*       a more convoluted example of the unsolvable nature of the problem,
*       see the Border class introduced in JFC/Swing).  To solve this lack
*       of uniformity in supporting classes, each class is individually
*       (i.e. manually) wrapped into a "representing" Component Definition;
*       for example, java.awt.Font is wrapped into Component.GUI.Font.  The
*       representing Component is responsible for two-way conversion of the
*       supporting class (e.g. java.awt.Font) as well as efficient mutability
*       of the implied sub-properties (e.g. name, style, and size).
*
*       The result is the capability to compile Text.Font.Size into Java
*       byte code operations that efficiently modify the Font object and then
*       update the Text control with the modified font:
*
*           aload Text
*           dup
*           invoke Component/GUI/Control.getFont()Component/GUI/Font
*           dup
*           bipush 24
*           invokevirtual Component/GUI/Font.setSize(I)V
*           invokevirtual Component/GUI/Control.setFont(Component/GUI/Font)V
*
*   3.  Persistence - Since complex Properties are implemented using the
*       Component class (which also implements the Component Definition data
*       structure), the persistence of a complex Property is the persistence
*       of the relevent Component information.  Complex Property values only
*       use a small subset of the Component implementation:
*
*           1.  Identity of a global Component (i.e. super and name)
*           2.  State (i.e. collection of Properties and values)
*
*       The other sections (Behavior, Aggregation) are not present in a
*       complex Property value, nor are the other traits of the Identity
*       section of the Component (such as Implements, Dispatches, etc.)
*       A complex Property value can only specify two types of information:
*
*           1.  The identity of the global Component Definition that the
*               complex value is based on
*           2.  The values for Properties of that Component
*
*       The persistence of a complex Property value is always as a
*       Component Modification.  This represents several technical issues:
*
*           1.  The Property, at its insert level, is stored resolved,
*               which means that the Property is not extracted from its
*               base (as there is no base to extract from); however, the
*               Property value, if complex, always has a base, and therefore
*               must be extracted.
*           2.  Two steps are involved in setting the value of a complex
*               Property:  selecting the Component which the value is based
*               on (subject to the Property data type constraint) and
*               optionally setting specific values.  Therefore, at any
*               derivation or modification level (which is therefore all
*               levels not covered by #1 above), it is possible to specify
*               what Component Definition the complex Property value is based
*               on, meaning that at any of these levels, the complex Property
*               value can represent a modification of a global Component
*               Definition as opposed to the modification of the base
*               Property's complex value.
*           3.  A complex Property value can contain complex Properties.
*               The solutions for the above scenarios must be recursive.
*
*       To resolve the first scenario, additional extract and resolve support
*       is required in both the Component Definition and the Property
*       implementations.  Specifically, the Component extract process either
*       invokes the Property extract process if the Property is not at its
*       insert level or it invokes a special "extract complex value only"
*       process if it is.
*
*       To resolve the second scenario, Property extract has to produce three
*       persistent pieces of information about the complex value:
*
*           1.  The identity of the Component Definition which the complex
*               value is based on
*           2.  Whether the identity of Component Definition that this
*               Property's complex value is based on is different from the
*               identity that the base Property's complex value is based on
*           3.  The State modification from the base complex value; this
*               modification is extracted from a global Component Definition
*               if the identities are different (see #2) or from the complex
*               value of the base Property if the identities are the same
*
*       Using these three pieces of information, Property resolve can
*       always re-assemble the Property value as follows:
*
*           1.  If this Property's complex value's identity matched the base
*               Property's complex value's identity when the value was
*               extracted (see #2 above), apply the modification to the
*               base complex value (even if the identities do not match
*               at resolve time)
*           2.  Otherwise, apply the modification to the global Component
*               Definition specified by the complex value modification
*
*       There is a minor exception to the above rules:  A complex Property
*       can have a value of null or NO_VALUE.  Again, the information of
*       whether the Component Definition identity of the complex value
*       differed at extract time gives a clear indication of whether the
*       null or NO_VALUE value should be propagated to this level or not.
*
*   4.  Initialization/Code Generation - Initialization of a complex
*       Property constructs an instance of the class resulting from the
*       Component Definition specified for the Property value and then
*       sets those sub-Properties that differ from the Component
*       Definition (i.e. applies the delta).  From the Font example above,
*       if the difference in State between Component.GUI.Font and
*       MyWin$Text.Font is simply that the size of MyWin$Text.Font is 24,
*       then the initialization code would be:
*
*           // this code is generated in __init to initialize the Font
*           // Property of this Component instance
*           Component.GUI.Font f = new Font
*           f.Size = 24
*           Font = f
*
*       Similar byte code is used to initialize the Property value regardless
*       of the Property category:
*       - Java constants are initialized in __initStatic (for static final
*         fields) or in __initPrivate (for final fields); the code is
*         identical in structure to that shown above.
*
*       - Virtual constants are initialized in the accessor immediately
*         before being returned:
*
*           Component.GUI.Font f = new Font
*           f.Size = 24
*           return f
*
* Relationships between Properties and Behaviors:
*
*   1.  Given a Property of type T and name N, the following Behavior
*       signatures are reserved:
*
*           Behavior Signature  Description
*           ------------------  ---------------------------------------------
*           setN(T)             Single setter, void return
*           setN(T[])           Indexed Property array setter, void return
*           setN(int, T)        Indexed Property index setter, void return
*           getN()              Singled or indexed Property array getter,
*                               T or T[] return type
*           getN(int)           Indexed Property index getter, T return type
*           isN()               Singled or indexed boolean Property array
*                               getter, boolean or boolean[] return type
*           isN(int)            Indexed boolean Property index getter,
*                               boolean return type
*
*       The reason for reserving all of the possible signatures is to avoid
*       the following problems:
*
*           1.  Having to limit the changes a user can make to the Property
*               declaration due to the collision the change would make.  For
*               example, if the super Component has a method isX and this
*               Component has a Property X of type int, there is no conflict.
*               However, were the type changed to boolean, there would be a
*               conflict.  To avoid this, Property X can be created if and
*               only if none of the corresponding Behavior signatures are
*               present at a super level.  (This does not mean that they
*               cannot be present at this level, for example as the result
*               of implementing an interface.  In other words, a Component
*               can implement an interface that contains the Behavior getX()
*               and can declare the Property X at the same level.  See the
*               exception to this rule under the Constant Properties section
*               below.)
*
*           2.  The corresponding Behavior signatures present an implied
*               contract to the user of the resulting bean.  The contract is
*               based on the Javabeans specification, which defines the
*               possible method signatures associated with a given property
*               name and type.  By reserving the Behaviors solely for the
*               purpose of the Property, there can be no way for the contract
*               (as interpreted by introspection) to be misinterpreted; any
*               methods that appear to be accessors for a given property are
*               certain to be just that.
*
*   2.  Constant Properties do not declare Behaviors but do reserve them.
*
*           1.  A Java constant results in the creation of a Java field in
*               the .class that results from the Component Definition that
*               declares it; (there is one possible exception:  private Java
*               constants of intrinsic types may be optimized out by the
*               compilation process.)
*
*           2.  A virtual constant results in the creation of a Java method
*               in the .class that results from the Component Definition that
*               declares it; (a virtual constant cannot be optimized out
*               because virtual constants are never private).  The method (or
*               methods, if the Property is indexed) is an accessor which
*               returns a constant (from the .class constant pool) value.
*               Each Component Definition that overrides the value of the
*               Property overrides the Java method to return the value for
*               that Component Definition
*
*       In either case above, the Behaviors are reserved, but cannot be
*       declared (and therefore cannot be implemented).
*
* @version 1.00, 11/12/97
* @author  Cameron Purdy
*/
public class Property
        extends    Trait
        implements Constants
    {
    // ----- construction ---------------------------------------------------

    /**
    * Constructor for all "manually" created Property objects.  This is the
    * "default" constructor.
    *
    * @param cd       the Component Definition containing this Property
    * @param nFlags   various Property attributes stored as bit flags
    * @param sName    the name of the Property
    * @param dt       the data type of the Property
    */
    protected Property(Component cd, int nFlags, String sName, DataType dt)
        {
        super(cd, RESOLVED);

        m_nFlags     = nFlags;
        m_sName      = sName;
        m_dt         = dt;
        m_oValue     = NO_VALUE;
        m_oPrevValue = NO_VALUE;

        // all properties of non-Signatures require a UID
        if (!cd.isSignature())
            {
            assignUID();
            }
        }

    /**
    * Construct a blank Property Trait.
    *
    * @param base   the base Property to derive from
    * @param cd     the containing Component
    * @param nMode  one of RESOLVED, DERIVATION, MODIFICATION
    *
    * @see #getBlankDerivedTrait(Trait, int)
    */
    protected Property(Property base, Component cd, int nMode)
        {
        super(base, cd, nMode);

        Object oVal = (nMode == RESOLVED ? NO_VALUE : NO_DELTA);

        this.m_nFlags     = EXISTS_UPDATE | base.m_nFlags & DECL_ONLY_MASK;
        this.m_sName      = base.m_sName;
        this.m_dt         = base.m_dt;
        this.m_oValue     = oVal;
        this.m_oPrevValue = oVal;
        }

    /**
    * Copy constructor to add an interface property.
    *
    * @param cd     the Component Definition containing the new Property
    * @param that   the Property to copy from
    * @param iface  the interface the property is coming from
    */
    protected Property(Component cd, Property that, Interface iface)
        {
        this(cd, that);
        setExists(EXISTS_NOT);
        addOriginTrait(iface);
        m_oPrevValue = m_oValue;
        }

    /**
    * Copy constructor.
    *
    * @param cd    the Component Definition containing the new Property
    * @param that  the Property to copy from
    */
    protected Property(Component cd, Property that)
        {
        super(cd, that);

        this.m_nFlags          = that.m_nFlags;
        this.m_sName           = that.m_sName;
        this.m_dt              = that.m_dt;
        this.m_oValue          = copyValue(that.m_oValue);
        this.m_oPrevValue      = copyValue(that.m_oPrevValue);
        this.m_fSizeDelta      = that.m_fSizeDelta;
        this.m_fPrevSizeDelta  = that.m_fPrevSizeDelta;
        }

    /**
    * Copy a value, used by the copy constructor to copy
    * the value from the original Property.
    *
    * @param oThat the value to be copied
    *
    * @return the copied value
    */
    private Object copyValue(Object oThat)
        {
        if (oThat == NO_DELTA || oThat == NO_VALUE || oThat == null)
            {
            return oThat;
            }

        if (isSingle())
            {
            if (!isComplex())
                {
                return oThat;
                }
            return new Component(this, (Component) oThat);
            }

        oThat = ((Object[]) oThat).clone();
        if (!isComplex())
            {
            return oThat;
            }

        Object[] aoThat = (Object[]) oThat;
        for (int i = 0; i < aoThat.length; ++i)
            {
            oThat = aoThat[i];
            if (oThat != NO_DELTA && oThat != NO_VALUE && oThat != null)
                {
                aoThat[i] = new Component(this, (Component) oThat);
                }
            }
        return aoThat;
        }

    /**
    * Construct a Java Class Signature (JCS) Property from field information.
    *
    * @param cdJCS  the containing JCS
    * @param field  the JASM field object
    */
    protected Property(Component cdJCS, Field field)
        {
        super(cdJCS, RESOLVED);

        // convert field information to Property flags
        int nFlags = EXISTS_INSERT | PROP_SINGLE;

        // access, visible
        if (field.isPublic())
            {
            nFlags |= ACCESS_PUBLIC | VIS_VISIBLE;
            }
        else if (field.isProtected())
            {
            nFlags |= ACCESS_PROTECTED | VIS_ADVANCED;
            }
        else if (field.isPackage())
            {
            nFlags |= ACCESS_PACKAGE | VIS_ADVANCED;
            }
        else if (field.isPrivate())
            {
            nFlags |= ACCESS_PRIVATE | VIS_ADVANCED;
            }

        // mark synthetic properties as "system"
        if (field.isSynthetic())
            {
            nFlags |= VIS_SYSTEM;
            }

        // deprecated
        nFlags |= (field.isDeprecated() ? ANTIQ_DEPRECATED : ANTIQ_CURRENT);

        // static
        nFlags |= (field.isStatic() ? SCOPE_STATIC : SCOPE_INSTANCE);

        // final fields are constants
        if (field.isFinal())
            {
            // constants are "final persistent out"
            nFlags |= DERIVE_FINAL | STG_PERSIST | DIR_OUT;
            }
        else
            {
            // non-constant are in/out
            nFlags |= DERIVE_DERIVABLE | DIR_INOUT;

            // storage
            nFlags |= (field.isTransient() ? STG_TRANSIENT : STG_PERSIST);
            }

        // determine Property value
        DataType dt = DataType.getJVMType(field.getType());
        Object oValue = NO_VALUE;
        if ((nFlags & (SCOPE_MASK | DERIVE_MASK)) == (SCOPE_STATIC | DERIVE_FINAL)
                && dt.isExtendedSimple() && field.isConstant())
            {
            // get the value for intrinsic+ constant (static final) fields
            Constant constant = field.getConstantValue();
            if (constant instanceof IntConstant)
                {
                switch (dt.getTypeString().charAt(0))
                    {
                    case 'B':
                        oValue = (byte) ((IntConstant) constant).getValue();
                        break;
                    case 'C':
                        oValue = (char) ((IntConstant) constant).getValue();
                        break;
                    case 'S':
                        oValue = (short) ((IntConstant) constant).getValue();
                        break;
                    case 'I':
                        oValue = ((IntConstant) constant).getValue();
                        break;
                    }
                }
            else if (constant instanceof LongConstant)
                {
                oValue = ((LongConstant) constant).getValue();
                }
            else if (constant instanceof FloatConstant)
                {
                oValue = ((FloatConstant) constant).getValue();
                }
            else if (constant instanceof DoubleConstant)
                {
                oValue = ((DoubleConstant) constant).getValue();
                }
            else if (constant instanceof StringConstant)
                {
                oValue = ((StringConstant) constant).getValue();
                }
            }

        // init Property info
        m_dt         = dt;
        m_sName      = field.getName();
        m_nFlags     = nFlags;
        m_oValue     = oValue;
        m_oPrevValue = NO_DELTA;
        }

    /**
    * Verify that two JCS fields do not conflict.
    *
    * @param that  the other property
    */
    protected void mergeJCSFields(Property that)
        {
        if (this.getExists() == EXISTS_DELETE)
            {
            // field is already ambiguous
            return;
            }

        if (   this.isFinal() && that.isFinal()
            && this.isStatic() && that.isStatic()
            && this.getDataType() == that.getDataType()
            && !this.isNoValue()
            && this.isValueEqual(that.m_oValue))
            {
            // field is not ambiguous because the constants agree
            // (chances are it is even the same constant)
            return;
            }

        this.setExists(EXISTS_DELETE);
        }

    /**
    * Construct the property from a stream.
    *
    * This is a custom serialization implementation that is unrelated to the
    * Serializable interface and the Java implementation of persistence.
    *
    * @param cd        the containing Component Definition
    * @param stream    the stream to read this property from
    * @param nVersion  version of the data structure in the stream
    *
    * @exception IOException An IOException is thrown if an error occurs
    *            reading the property information from the stream
    */
    protected Property(Component cd, DataInput stream, int nVersion)
            throws IOException
        {
        super(cd, stream, nVersion);

        // various attributes stored as bit flags
        m_nFlags = stream.readInt();

        // Property name
        m_sName = stream.readUTF();

        // Data Type
        m_dt = DataType.getType(stream.readUTF());

        // size delta (applicable for der/mod only)
        m_fSizeDelta = stream.readBoolean();

        // Property value and default value
        boolean fSingle = isSingle();
        byte    nType   = getDataTypeEnum();
        m_oValue     = readValue(stream, nType, fSingle, nVersion);
        m_oPrevValue = readValue(stream, nType, fSingle, nVersion);

        // TODO:  Remove when all is determined to be okay...
        // fixup broken JCS'es
        //if (cd.isSignature()
        //    && m_oPrevValue == null)
            {
            m_oPrevValue = NO_DELTA;
            }
        }

    /**
    * Construct a Trait object from persistent data.  All traits implement
    * implement a constructor from persistent data.
    *
    * @param parent    the trait that contains this trait
    * @param xml       the XmlElement to read the persistent information from
    * @param nVersion  version of the data structure in the stream
    *
    * @throws IOException if an exception occurs reading the trait data
    */
    protected Property(Trait parent, XmlElement xml, int nVersion)
            throws IOException
        {
        super(parent, xml, nVersion);

        String sName = readString(xml.getElement("name"));
        String sType = readString(xml.getElement("type"));
        if (sName == BLANK || sType == BLANK)
            {
            throw new IOException("name or type is missing");
            }
        int nFlags = readFlags(xml, "flags", DIR_IN);

        // size delta (applicable for der/mod only)
        boolean fSizeDelta = false;
        XmlElement xmlSizeDelta = xml.getElement("size-delta");
        if (xmlSizeDelta != null)
            {
            fSizeDelta = xmlSizeDelta.getBoolean();
            }

        m_sName      = sName;
        m_dt         = DataType.getType(sType);
        m_nFlags     = nFlags;
        m_fSizeDelta = fSizeDelta;

        // Property value and default value
        boolean fSingle  = isSingle();
        byte    nType    = getDataTypeEnum();
        Object  oDefault = (getMode() == RESOLVED ? NO_VALUE : NO_DELTA);
        m_oValue     = readValue(xml, "current", nType, fSingle, nVersion, oDefault);
        m_oPrevValue = readValue(xml, "original", nType, fSingle, nVersion, oDefault);
        }


    // ----- persistence ----------------------------------------------------

    /**
    * Save the property to a stream.
    *
    * This is a custom serialization implementation that is unrelated to the
    * Serializable interface and the Java implementation of persistence.
    *
    * @param stream   the stream to write this property to
    *
    * @exception IOException An IOException is thrown if an error occurs
    *            writing the property contents to the stream
    */
    protected synchronized void save(DataOutput stream)
            throws IOException
        {
        super.save(stream);

        // various attributes stored as bit flags
        stream.writeInt(m_nFlags);

        // Property name
        stream.writeUTF(m_sName);

        // Data Type
        stream.writeUTF(m_dt.getTypeString());

        // size delta (applicable for der/mod only)
        stream.writeBoolean(m_fSizeDelta);

        // Property value and default value
        boolean fSingle = isSingle();
        byte    nType   = getDataTypeEnum();
        writeValue(stream, nType, fSingle, m_oValue);
        writeValue(stream, nType, fSingle, m_oPrevValue);
        }

    /**
    * Save the Trait information to XML.  If derived traits have any
    * data of their own, then they must implement (i.e. supplement) this
    * method.
    *
    * This is a custom serialization implementation that is unrelated to the
    * Serializable interface and the Java implementation of persistence.
    *
    * @param xml  an XmlElement to write the persistent information to
    *
    * @throws IOException if an exception occurs saving the trait data
    */
    protected synchronized void save(XmlElement xml)
            throws IOException
        {
        xml.addElement("name").setString(m_sName);
        xml.addElement("type").setString(m_dt.getTypeString());

        super.save(xml);

        saveFlags(xml, "flags", m_nFlags, 0);

        // REVIEW: jhowes 2007.10.18:
        // Added a human-readable description attribute to the flags element
        XmlElement xmlFlags = xml.getElement("flags");
        if (xmlFlags != null)
            {
            xmlFlags.addAttribute("desc").setString(flagsDescription(m_nFlags,
                    PROP_SPECIFIED | SCOPE_SPECIFIED, false));
            }

        // size delta (applicable for der/mod only)
        if (m_fSizeDelta)
            {
            xml.addElement("size-delta").setBoolean(true);
            }

        // Property value and default value
        boolean fSingle  = isSingle();
        byte    nType    = getDataTypeEnum();
        Object  oDefault = (getMode() == RESOLVED ? NO_VALUE : NO_DELTA);
        writeValue(xml, "current", nType, fSingle, m_oValue, oDefault);
        writeValue(xml, "original", nType, fSingle, m_oPrevValue, oDefault);
        }

    /**
    * Read a complete Property value from a stream, including an entire
    * indexed Property value.
    *
    * @param stream    the stream to read the value from
    * @param nType     the enum type of Property to read
    * @param fSingle   if the value to read is expected to be (i.e. must be)
    *                  a single value
    * @param nVersion  version of the data structure in the stream
    *
    * @return the object that was read
    *
    * @exception IOException An IOException is thrown if an error occurs
    *            reading the property information from the stream
    */
    protected Object readValue(DataInput stream, byte nType, boolean fSingle, int nVersion)
            throws IOException
        {
        boolean fMulti = stream.readBoolean();
        if (fMulti)
            {
            int c = stream.readInt();
            Object[] ao = new Object[c];
            for (int i = 0; i < c; ++i)
                {
                ao[i] = readValue(stream, nType, nVersion);
                }
            return ao;
            }
        else
            {
            return readValue(stream, nType, nVersion);
            }
        }

    /**
    * Read a complete Property value from XML, including an entire
    * indexed Property value.
    *
    * @param xml       the XmlElement to read the value from
    * @param sElement  the name of the element to store the value under
    * @param nType     the enum type of Property to read
    * @param fSingle   if the value to read is expected to be (i.e. must be)
    *                  a single value
    * @param nVersion  version of the data structure in the XML
    * @param oDefault  the default Property value
    *
    * @return the object that was read
    *
    * @exception IOException An IOException is thrown if an error occurs
    *            reading the property information from the XML
    */
    protected Object readValue(XmlElement xml, String sElement, byte nType, boolean fSingle, int nVersion, Object oDefault)
            throws IOException
        {
        xml = xml.getElement(sElement);
        if (xml == null)
            {
            return oDefault;
            }

        // there are several possibilities:
        // 1) multi would have some number (0+?) of  sub-elements
        // 2) single could have a value
        // 3) single could have a  and  sub-element
        boolean fMulti = xml.getString(null) == null && xml.getElement("type") == null;
        if (fMulti)
            {
            // walk through list of  elements
            List listElements = xml.getElementList();
            List listValues = new ArrayList();
            for (Iterator iter = listElements.iterator(); iter.hasNext(); )
                {
                XmlElement xmlElement = (XmlElement) iter.next();
                if (xmlElement.getName().equals("element"))
                    {
                    listValues.add(readValue(xmlElement, nType, nVersion));
                    }
                }
            return listValues.toArray();
            }
        else
            {
            return readValue(xml, nType, nVersion);
            }
        }

    /**
    * Read a single Property value from a stream.
    *
    * @param stream    the stream to read the value from
    * @param nType     the enum type of Property to read
    * @param nVersion  version of the data structure in the stream
    *
    * @return the next value from the stream
    *
    * @exception IOException An IOException is thrown if an error occurs
    *            reading the property information from the stream
    */
    protected Object readValue(DataInput stream, byte nType, int nVersion)
            throws IOException
        {
        byte b = stream.readByte();

        switch (b)
            {
            case E_NO_VALUE:
                return NO_VALUE;

            case E_NO_DELTA:
                return NO_DELTA;

            case E_NULL_REF:
                return null;

            case E_HAS_VALUE:
                break;

            default:
                throw new IOException(CLASS + ".readValue:  Unexpected value specifier.");
            }

        switch (nType)
            {
            case DT_BOOLEAN:
                return stream.readBoolean();

            case DT_CHAR:
                return stream.readChar();

            case DT_BYTE:
                return stream.readByte();

            case DT_SHORT:
                return stream.readShort();

            case DT_INT:
                return stream.readInt();

            case DT_LONG:
                return stream.readLong();

            case DT_FLOAT:
                return stream.readFloat();

            case DT_DOUBLE:
                return stream.readDouble();

            case DT_BINARY:
                {
                int     cb = stream.readInt();
                byte[]  ab = new byte[cb];
                stream.readFully(ab);
                return ab;
                }

            case DT_STRING:
                return stream.readUTF();

            case DT_COMPLEX:
                return new Component(this, stream, nVersion);

            case DT_SERIALIZABLE:
                {
                int     cb = stream.readInt();
                byte[]  ab = new byte[cb];
                stream.readFully(ab);

                ByteArrayInputStream streamRaw = new ByteArrayInputStream(ab);
                ObjectInputStream    streamObj = new ObjectInputStream(streamRaw);
                try
                    {
                    Object o = streamObj.readObject();
                    streamObj.close();
                    return o;
                    }
                catch (ClassNotFoundException e)
                    {
                    throw new IOException(e.toString());
                    }
                }

            default:
                throw new IOException(CLASS + ".readValue:  Unexpected value.");
            }
        }

    /**
    * Read a single Property value from a stream.
    *
    * @param xml       the XML to read the value from
    * @param nType     the enum type of Property to read
    * @param nVersion  version of the data structure in the stream
    *
    * @return the next value from the XML
    *
    * @exception IOException An IOException is thrown if an error occurs
    *            reading the property information from the XML
    */
    protected Object readValue(XmlElement xml, byte nType, int nVersion)
            throws IOException
        {
        Object oValue = null;

        String sValue = xml.getString(null);
        if (sValue == null)
            {
            XmlElement xmlType  = xml.getElement("type");
            XmlElement xmlValue = xml.getElement("value");
            if (xmlType == null || xmlValue == null)
                {
                throw new IOException("missing  and/or  tags for property value");
                }

            String sType = xmlType.getString();
            if (sType.length() == 0)
                {
                throw new IOException("missing  tag for property value");
                }

            switch (sType.charAt(0))
                {
                case 'b':
                    if (sType.equals("boolean"))
                        {
                        oValue = (xmlValue.getBoolean() ? Boolean.TRUE : Boolean.FALSE);
                        }
                    else if (sType.equals("byte"))
                        {
                        oValue = (byte) xmlValue.getInt();
                        }
                    else if (sType.equals("binary"))
                        {
                        oValue = xmlValue.getBinary().toByteArray();
                        }
                    break;

                case 'c':
                    if (sType.equals("char"))
                        {
                        oValue = (char) xmlValue.getInt();
                        }
                    else if (sType.equals("complex"))
                        {
                        oValue = new Component(this, xmlValue, nVersion);
                        }
                    break;

                case 'd':
                    if (sType.equals("double"))
                        {
                        oValue = xmlValue.getDouble();
                        }
                    break;

                case 'f':
                    if (sType.equals("float"))
                        {
                        oValue = (float) xmlValue.getDouble();
                        }
                    break;

                case 'i':
                    if (sType.equals("int"))
                        {
                        oValue = xmlValue.getInt();
                        }
                    break;

                case 'l':
                    if (sType.equals("long"))
                        {
                        oValue = xmlValue.getLong();
                        }
                    break;

                case 's':
                    if (sType.equals("short"))
                        {
                        oValue = (short) xmlValue.getInt();
                        }
                    else if (sType.equals("string"))
                        {
                        oValue = xmlValue.getString();
                        }
                    else if (sType.equals("serializable"))
                        {
                        byte[] ab = xmlValue.getBinary().toByteArray();
                        ByteArrayInputStream streamRaw = new ByteArrayInputStream(ab);
                        ObjectInputStream    streamObj = new ObjectInputStream(streamRaw);
                        try
                            {
                            oValue = streamObj.readObject();
                            }
                        catch (ClassNotFoundException e)
                            {
                            throw new IOException(e.toString());
                            }
                        finally
                            {
                            streamObj.close();
                            }
                        }
                    break;
                }

            if (oValue == null)
                {
                throw new IOException("invalid type and/or value (type=" + sType + ")");
                }
            }
        else if (sValue.equals("null"))
            {
            oValue = null;
            }
        else if (sValue.equals("none"))
            {
            oValue = NO_VALUE;
            }
        else if (sValue.equals("unchanged"))
            {
            oValue = NO_DELTA;
            }

        return oValue;
        }

    /**
    * Write a complete Property value to a stream, including an entire
    * indexed Property value.
    *
    * @param stream   the stream to write the value to
    * @param nType    the enum type of Property to write
    * @param fSingle  if the value to write is definitely a single value
    *                 (i.e. the Property is not declared as indexed)
    * @param o        the Property value to write
    *
    * @exception IOException An IOException is thrown if an error occurs
    *            writing the value to the stream
    */
    protected void writeValue(DataOutput stream, byte nType, boolean fSingle, Object o)
            throws IOException
        {
        boolean fMulti = !(fSingle || o == NO_VALUE || o == NO_DELTA || o == null);
        stream.writeBoolean(fMulti);

        if (fMulti)
            {
            Object[] ao = (Object[]) o;
            int c = ao.length;

            stream.writeInt(c);
            for (int i = 0; i < c; ++i)
                {
                writeValue(stream, nType, ao[i]);
                }
            }
        else
            {
            writeValue(stream, nType, o);
            }
        }

    /**
    * Write a complete Property value to a stream, including an entire
    * indexed Property value.
    *
    * @param xml       the XML element for the property to write the value to
    * @param sElement  the name of the element to store the value under
    * @param nType     the enum type of Property to write
    * @param fSingle   if the value to write is definitely a single value
    *                  (i.e. the Property is not declared as indexed)
    * @param o         the Property value to write
    * @param oDefault  the default Property value
    *
    * @exception IOException An IOException is thrown if an error occurs
    *            writing the value to the stream
    */
    protected void writeValue(XmlElement xml, String sElement, byte nType, boolean fSingle, Object o, Object oDefault)
            throws IOException
        {
        if (o == oDefault)
            {
            return;
            }

        xml = xml.addElement(sElement);

        boolean fMulti = !(fSingle || o == NO_VALUE || o == NO_DELTA || o == null);

        if (fMulti)
            {
            Object[] ao = (Object[]) o;
            int c = ao.length;

            for (int i = 0; i < c; ++i)
                {
                writeValue(xml.addElement("element"), nType, ao[i]);
                }
            }
        else
            {
            writeValue(xml, nType, o);
            }
        }

    /**
    * Write a Property value to a stream.
    *
    * @param stream  the stream to write the value to
    * @param nType   the enum type of Property to write
    * @param o       the value to write
    *
    * @exception IOException An IOException is thrown if an error occurs
    *            writing the value to the stream
    */
    protected void writeValue(DataOutput stream, byte nType, Object o)
            throws IOException
        {
        // determine if the value is a "special value" (no value, no delta, null)
        byte b;

        if (o == null)
            {
            b = E_NULL_REF;
            }
        else if (o == NO_VALUE)
            {
            b = E_NO_VALUE;
            }
        else if (o == NO_DELTA)
            {
            b = E_NO_DELTA;
            }
        else
            {
            b = E_HAS_VALUE;
            }

        stream.writeByte(b);

        // if it isn't a special value, then write the exact value
        if (b == E_HAS_VALUE)
            {
            switch (nType)
                {
                case DT_BOOLEAN:
                    stream.writeBoolean(((Boolean  ) o).booleanValue());
                    break;

                case DT_CHAR:
                    stream.writeChar   (((Character) o).charValue   ());
                    break;

                case DT_BYTE:
                    stream.writeByte   (((Number   ) o).byteValue   ());
                    break;

                case DT_SHORT:
                    stream.writeShort  (((Number   ) o).shortValue  ());
                    break;

                case DT_INT:
                    stream.writeInt    (((Number   ) o).intValue    ());
                    break;

                case DT_LONG:
                    stream.writeLong   (((Number   ) o).longValue   ());
                    break;

                case DT_FLOAT:
                    stream.writeFloat  (((Number   ) o).floatValue  ());
                    break;

                case DT_DOUBLE:
                    stream.writeDouble (((Number   ) o).doubleValue ());
                    break;

                case DT_BINARY:
                    {
                    byte[] ab = (byte[]) o;
                    stream.writeInt(ab.length);
                    stream.write(ab);
                    }
                    break;

                case DT_STRING:
                    stream.writeUTF((String) o);
                    break;

                case DT_COMPLEX:
                    ((Component) o).save(stream);
                    break;

                case DT_SERIALIZABLE:
                    {
                    ByteArrayOutputStream streamRaw = new ByteArrayOutputStream();
                    ObjectOutputStream    streamObj = new ObjectOutputStream(streamRaw);
                    streamObj.writeObject((Serializable) o);
                    streamObj.flush();
                    streamObj.close();

                    byte[] ab = streamRaw.toByteArray();
                    stream.writeInt(ab.length);
                    stream.write(ab);
                    }
                    break;

                default:
                    throw new IOException(CLASS + ".writeValue:  Unexpected value.");
                }
            }
        }

    /**
    * Write a Property value to an XML element.
    *
    * @param xml    the XML element to write the value to
    * @param nType  the enum type of Property to write
    * @param o      the value to write
    *
    * @exception IOException An IOException is thrown if an error occurs
    *            writing the value to the stream
    */
    protected void writeValue(XmlElement xml, byte nType, Object o)
            throws IOException
        {
        // determine if the value is a "special value" (no value, no delta, null)
        if (o == null)
            {
            xml.setString("null");
            }
        else if (o == NO_VALUE)
            {
            xml.setString("none");
            }
        else if (o == NO_DELTA)
            {
            xml.setString("unchanged");
            }
        else
            {
            XmlElement xmlType  = xml.addElement("type");
            XmlElement xmlValue = xml.addElement("value");

            // if it isn't a special value, then write the exact value
            switch (nType)
                {
                case DT_BOOLEAN:
                    xmlType.setString("boolean");
                    xmlValue.setBoolean(((Boolean  ) o).booleanValue());
                    break;

                case DT_CHAR:
                    xmlType.setString("char");
                    xmlValue.setInt(((Character) o).charValue());
                    break;

                case DT_BYTE:
                    xmlType.setString("byte");
                    xmlValue.setInt(((Number) o).byteValue());
                    break;

                case DT_SHORT:
                    xmlType.setString("short");
                    xmlValue.setInt(((Number) o).shortValue());
                    break;

                case DT_INT:
                    xmlType.setString("int");
                    xmlValue.setInt(((Number) o).intValue());
                    break;

                case DT_LONG:
                    xmlType.setString("long");
                    xmlValue.setLong(((Number) o).longValue());
                    break;

                case DT_FLOAT:
                    xmlType.setString("float");
                    xmlValue.setDouble(((Number) o).doubleValue());
                    break;

                case DT_DOUBLE:
                    xmlType.setString("double");
                    xmlValue.setDouble(((Number) o).doubleValue());
                    break;

                case DT_BINARY:
                    {
                    xmlType.setString("binary");
                    byte[] ab = (byte[]) o;
                    xmlValue.setBinary(new Binary(ab));
                    }
                    break;

                case DT_STRING:
                    xmlType.setString("string");
                    xmlValue.setString((String) o);
                    break;

                case DT_COMPLEX:
                    xmlType.setString("complex");
                    ((Component) o).save(xmlValue);
                    break;

                case DT_SERIALIZABLE:
                    {
                    xmlType.setString("serializable");

                    ByteArrayOutputStream streamRaw = new ByteArrayOutputStream();
                    ObjectOutputStream    streamObj = new ObjectOutputStream(streamRaw);
                    streamObj.writeObject((Serializable) o);
                    streamObj.flush();
                    streamObj.close();

                    byte[] ab = streamRaw.toByteArray();
                    xmlValue.setBinary(new Binary(ab));
                    }
                    break;

                default:
                    throw new IOException(CLASS + ".writeValue:  Unexpected value.");
                }
            }
        }


    // ----- construction (helpers)  ----------------------------------------

    /**
    * Determine if the specified Property can be created.
    *
    * @param cd         the Component Definition containing this Property
    * @param dt         the data type of the Property
    * @param sName      the name of the Property
    * @param nIndexed   specifies whether the Property is indexed, one of
    *                   PROP_SINGLE, PROP_INDEXED, or PROP_INDEXEDONLY; see
    *                   the Java Beans specification (7.2)
    * @param fStatic    true if the Property will be static
    * @param nDir       DIR_IN, DIR_OUT, or DIR_INOUT
    * @param fConstant  true if the Property will be constant
    *
    * @return true if the Property can be created
    */
    protected static boolean isCreatable(Component cd, DataType dt, String sName, int nIndexed, boolean fStatic, int nDir, boolean fConstant)
        {
        // Component Definition must be modifiable
        if (!cd.isModifiable())
            {
            return false;
            }

        // must be a legal identifier
        if (sName == null || sName.length() == 0 ||
                !ClassHelper.isSimpleNameLegal(sName))
            {
            return false;
            }

        // check for conflict with other Property names
        Property prop = cd.getProperty(sName);
        if (prop != null)
            {
            return false;
            }

        // if this is a Java Class Signature, then don't worry
        // about conficts with existing methods
        if (cd.isSignature())
            {
            return true;
            }

        // check for conflict with existing Behaviors:
        String[]  asBehavior = getReservedBehaviorSignatures(dt, sName);
        Behavior[] aBehavior = getReservedBehaviors(cd, dt, sName);
        int        cBehavior = aBehavior.length;
        for (int i = 0; i < cBehavior; ++i)
            {
            Behavior behavior = aBehavior[i];
            if (behavior != null)
                {
                // constants cannot have accessors
                if (fConstant)
                    {
                    return false;
                    }

                // accessor Behavior names are case sensitive
                String sBehavior = asBehavior[i];
                sBehavior = sBehavior.substring(0,sBehavior.indexOf('('));
                if (!behavior.getName().equals(sBehavior))
                    {
                    if (!(behavior.isNameSettable() && behavior.isNameLegal(sBehavior)))
                        {
                        return false;
                        }
                    }

                // is the behavior supposed to exist?
                DataType dtRet = null;
                switch (i)
                    {
                    case RSVD_SET_SINGLE:
                        // settable, single accessor
                        if (!((nDir & DIR_IN) != 0 && nIndexed == PROP_SINGLE))
                            {
                            return false;
                            }
                        dtRet = DataType.VOID;
                        break;

                    case RSVD_SET_ARRAY:
                        // settable, array accessor
                        if (!((nDir & DIR_IN) != 0 && nIndexed == PROP_INDEXED))
                            {
                            return false;
                            }
                        dtRet = DataType.VOID;
                        break;

                    case RSVD_SET_INDEX:
                        // settable, indexed accessor
                        if (!((nDir & DIR_IN) != 0 && (nIndexed & PROP_INDEXEDONLY) != 0))
                            {
                            return false;
                            }
                        dtRet = DataType.VOID;
                        break;

                    case RSVD_GET_EITHER:
                        // gettable, single/array accessor, not boolean
                        if (!((nDir & DIR_OUT) != 0 && (nIndexed & PROP_SINGLE) != 0
                                && dt != BOOLEAN))
                            {
                            return false;
                            }
                        dtRet = (nIndexed == PROP_SINGLE ? dt : dt.getArrayType());
                        break;

                    case RSVD_IS_EITHER:
                        // gettable, single/array accessor, boolean
                        if (!((nDir & DIR_OUT) != 0 && (nIndexed & PROP_SINGLE) != 0
                                && dt == BOOLEAN))
                            {
                            return false;
                            }
                        dtRet = (nIndexed == PROP_SINGLE ? dt : dt.getArrayType());
                        break;

                    case RSVD_GET_INDEX:
                        // gettable, indexed accessor, not boolean
                        if (!((nDir & DIR_OUT) != 0 && (nIndexed & PROP_INDEXEDONLY) != 0
                                && dt != BOOLEAN))
                            {
                            return false;
                            }
                        dtRet = dt;
                        break;

                    case RSVD_IS_INDEX:
                        // gettable, indexed accessor, boolean
                        if (!((nDir & DIR_OUT) != 0 && (nIndexed & PROP_INDEXEDONLY) != 0
                                && dt == BOOLEAN))
                            {
                            return false;
                            }
                        dtRet = dt;
                        break;
                    }

                // does the behavior have an origin of super?
                if (behavior.isFromSuper())
                    {
                    return false;
                    }

                // does the behavior match the required declaration or is it
                // capable of being modified to match
                if (behavior.isStatic() != fStatic && !behavior.isStaticSettable())
                    {
                    return false;
                    }
                if (behavior.getReturnValue().getDataType() != dtRet)
                    {
                    ReturnValue retval = behavior.getReturnValue();
                    if (!(retval.isDataTypeSettable() && retval.isDataTypeLegal(dtRet)))
                        {
                        return false;
                        }
                    }
                }
            }

        return true;
        }

    /**
    * Determine if the specified Java constant Property can be created.
    *
    * @param cd        the Component Definition containing this Property
    * @param dt        the data type of the Property
    * @param sName     the name of the Property
    * @param nIndexed  specifies whether the Property is indexed, one of
    *                  PROP_SINGLE or PROP_INDEXED
    * @param fStatic   true if the Java constant is static (static final)
    * @param nAccess   accessibility of the Java constant
    *
    * @return true if the specified Property can be created
    */
    protected static boolean isJavaConstantCreatable(Component cd, DataType dt, String sName, int nIndexed, boolean fStatic, int nAccess)
        {
        // Java constants cannot be indexed-only
        if (nIndexed == PROP_INDEXEDONLY)
            {
            return false;
            }

        return isCreatable(cd, dt, sName, nIndexed, fStatic, DIR_OUT, true);
        }

    /**
    * Create a Java constant Property.  A constant field is generated in the
    * Java class that results from the Component Definition that declares the
    * constant.  The Property is declared as "persistent final out" and the
    * resulting Java field is "final".
    *
    * @param cd        the Component Definition containing this Property
    * @param dt        the data type of the Property
    * @param sName     the name of the Property
    * @param nIndexed  specifies whether the Property is indexed, one of
    *                  PROP_SINGLE or PROP_INDEXED
    * @param fStatic   true if the Java constant is static (static final)
    * @param nAccess   accessibility of the Java constant
    */
    protected static Property createJavaConstant(Component cd, DataType dt, String sName, int nIndexed, boolean fStatic, int nAccess)
        {
        int nScope = (fStatic ? SCOPE_STATIC : SCOPE_INSTANCE);
        int nFlags = DEFAULT_FLAGS |
                     EXISTS_INSERT |
                     STG_PERSIST   |
                     DERIVE_FINAL  |
                     DIR_OUT       |
                     nIndexed      |
                     nScope        |
                     nAccess;

        return new Property(cd, nFlags, sName, dt);
        }

    /**
    * Determine if the specified virtual constant Property can be created.
    *
    * @param cd        the Component Definition containing this Property
    * @param dt        the data type of the Property
    * @param sName     the name of the Property
    * @param nIndexed  specifies whether the Property is indexed, one of
    *                  PROP_SINGLE, PROP_INDEXED, or PROP_INDEXEDONLY
    * @param nAccess   accessibility of the virtual constant
    *
    * @return true if the specified Property can be created
    */
    protected static boolean isVirtualConstantCreatable(Component cd, DataType dt, String sName, int nIndexed, int nAccess)
        {
        return isCreatable(cd, dt, sName, nIndexed, false, DIR_OUT, true);
        }

    /**
    * Create a virtual constant Property,  The property is declared as
    * "persistent instance derivable out" and no Java field results.  No
    * accessor Behavior(s) corresponding to the virtual constant are
    * declared, but they are reserved.
    *
    * @param cd        the Component Definition containing this Property
    * @param dt        the data type of the Property
    * @param sName     the name of the Property
    * @param nIndexed  specifies whether the Property is indexed, one of
    *                  PROP_SINGLE, PROP_INDEXED, or PROP_INDEXEDONLY
    * @param nAccess   accessibility of the virtual constant
    */
    protected static Property createVirtualConstant(Component cd, DataType dt, String sName, int nIndexed, int nAccess)
        {
        int nFlags = DEFAULT_FLAGS    |
                     EXISTS_INSERT    |
                     STG_PERSIST      |
                     SCOPE_INSTANCE   |
                     DERIVE_DERIVABLE |
                     DIR_OUT          |
                     nIndexed         |
                     nAccess;

        return new Property(cd, nFlags, sName, dt);
        }

    /**
    * Determine if the specified calculated Property can be created.
    *
    * @param cd        the Component Definition containing this Property
    * @param dt        the data type of the Property
    * @param sName     the name of the Property
    * @param nIndexed  specifies whether the Property is indexed, one of
    *                  PROP_SINGLE, PROP_INDEXED, or PROP_INDEXEDONLY
    * @param fStatic   true if the Java constant is static (static final)
    *
    * @return true if the specified Property can be created
    */
    protected static boolean isCalculatedPropertyCreatable(Component cd, DataType dt, String sName, int nIndexed, boolean fStatic)
        {
        return isCreatable(cd, dt, sName, nIndexed, fStatic, DIR_OUT, false);
        }

    /**
    * Create a calculated Property.  A calculated Property is declared as
    * "transient out", which means no corresponding Java field is generated.
    * A calculated Property is implemented by the developer to calculate some
    * value based on, perhaps, other Properties.
    *
    * @param cd        the Component Definition containing this Property
    * @param dt        the data type of the Property
    * @param sName     the name of the Property
    * @param nIndexed  specifies whether the Property is indexed, one of
    *                  PROP_SINGLE, PROP_INDEXED, or PROP_INDEXEDONLY
    * @param fStatic   true if the Java constant is static (static final)
    */
    protected static Property createCalculatedProperty(Component cd, DataType dt, String sName, int nIndexed, boolean fStatic)
        {
        int nScope = (fStatic ? SCOPE_STATIC : SCOPE_INSTANCE);
        int nFlags = DEFAULT_FLAGS    |
                     EXISTS_INSERT    |
                     ACCESS_PRIVATE   |
                     DERIVE_DERIVABLE |
                     STG_TRANSIENT    |
                     DIR_OUT          |
                     nIndexed         |
                     nScope;

        Property property = new Property(cd, nFlags, sName, dt);
        property.addAccessors();

        return property;
        }

    /**
    * Determine if the specified functional Property can be created.
    *
    * @param cd        the Component Definition containing this Property
    * @param dt        the data type of the Property
    * @param sName     the name of the Property
    * @param nIndexed  specifies whether the Property is indexed, one of
    *                  PROP_SINGLE, PROP_INDEXED, or PROP_INDEXEDONLY
    * @param fStatic   true if the Java constant is static (static final)
    *
    * @return true if the specified Property can be created
    */
    protected static boolean isFunctionalPropertyCreatable(Component cd, DataType dt, String sName, int nIndexed, boolean fStatic)
        {
        return isCreatable(cd, dt, sName, nIndexed, fStatic, DIR_IN, false);
        }

    /**
    * Create a functional Property.  Properties which are settable-only
    * (declared as "in") are considered to be functional.  A functional
    * Property is implemented by the developer and has no corresponding Java
    * field.  Functional Properties are not gettable, but they do appear in
    * the property sheet at design time, they are persisted in the Component
    * Definition, and they are initialized (which invokes the setter).
    *
    * @param cd        the Component Definition containing this Property
    * @param dt        the data type of the Property
    * @param sName     the name of the Property
    * @param nIndexed  specifies whether the Property is indexed, one of
    *                  PROP_SINGLE, PROP_INDEXED, or PROP_INDEXEDONLY
    * @param fStatic   true if the Java constant is static (static final)
    */
    protected static Property createFunctionalProperty(Component cd, DataType dt, String sName, int nIndexed, boolean fStatic)
        {
        int nScope = (fStatic ? SCOPE_STATIC : SCOPE_INSTANCE);
        int nFlags = DEFAULT_FLAGS    |
                     EXISTS_INSERT    |
                     ACCESS_PRIVATE   |
                     DERIVE_DERIVABLE |
                     STG_TRANSIENT    |
                     DIR_IN           |
                     nIndexed         |
                     nScope;

        Property property = new Property(cd, nFlags, sName, dt);
        property.addAccessors();

        return property;
        }

    /**
    * Determine if the specified standard Property can be created.
    *
    * @param cd        the Component Definition containing this Property
    * @param dt        the data type of the Property
    * @param sName     the name of the Property
    * @param nIndexed  specifies whether the Property is indexed, one of
    *                  PROP_SINGLE, PROP_INDEXED, or PROP_INDEXEDONLY
    * @param fStatic   true if the Java constant is static (static final)
    * @param fPersist  true if the resulting Java field should be serialized
    *                  by the Java serialization mechanism
    *
    * @return true if the specified Property can be created
    */
    protected static boolean isPropertyCreatable(Component cd, DataType dt, String sName, int nIndexed, boolean fStatic, boolean fPersist)
        {
        return isCreatable(cd, dt, sName, nIndexed, fStatic, DIR_INOUT, false);
        }

    /**
    * Create a standard Property.  Standard Properties are declared as
    * "inout".  A Java field is generated for standard Properties if storage
    * is required.  Java methods are auto-generated for non-private standard
    * Properties if they are declared but not implemented; the value of a
    * private standard Property can be accessed using a get or set field
    * operation if the corresponding accessor is not implemented.
    *
    * @param cd        the Component Definition containing this Property
    * @param dt        the data type of the Property
    * @param sName     the name of the Property
    * @param nIndexed  specifies whether the Property is indexed, one of
    *                  PROP_SINGLE, PROP_INDEXED, or PROP_INDEXEDONLY
    * @param fStatic   true if the Java constant is static (static final)
    * @param fPersist  true if the resulting Java field should be serialized
    *                  by the Java serialization mechanism
    */
    protected static Property createProperty(Component cd, DataType dt, String sName, int nIndexed, boolean fStatic, boolean fPersist)
        {
        int nScope = (fStatic  ? SCOPE_STATIC : SCOPE_INSTANCE);
        int nStg   = (fPersist ? STG_PERSIST  : STG_TRANSIENT );
        int nFlags = DEFAULT_FLAGS    |
                     EXISTS_INSERT    |
                     ACCESS_PRIVATE   |
                     DERIVE_DERIVABLE |
                     DIR_INOUT        |
                     nIndexed         |
                     nScope           |
                     nStg;

        Property property = new Property(cd, nFlags, sName, dt);
        property.addAccessors();

        return property;
        }


    // ----- derivation/modification ----------------------------------------

    /**
    * Construct a blank Property from this base.
    *
    * @param parent  the containing Component
    * @param nMode   RESOLVED, DERIVATION or MODIFICATION
    *
    * @return a new blank derived trait of this trait's class with the
    *         specified parent and mode
    */
    protected Trait getBlankDerivedTrait(Trait parent, int nMode)
        {
        return new Property(this, (Component) parent, nMode);
        }

    /**
    * Create a derived/modified Trait by combining the information from this
    * Trait with the super or base level's Trait information.  Neither the
    * base ("this") nor the delta may be modified in any way.
    *
    * @param traitDelta  the Trait derivation or modification
    * @param parent      the Trait which will contain the resulting Trait or
    *                    null if the resulting Trait will not be contained
    * @param loader      the Loader object for JCS, CIM, and CD dependencies
    * @param errlist     the error list object to log error information to
    *
    * @return the result of applying the specified delta Trait to this Trait
    *
    * @exception ComponentException  thrown only if a fatal error occurs
    */
    protected Trait resolve(Trait traitDelta, Trait parent, Loader loader, ErrorList errlist)
            throws ComponentException
        {
        Property base    = this;
        Property delta   = (Property) resolveDelta(traitDelta, loader, errlist);
        Property derived = (Property) super.resolve(delta, parent, loader, errlist);

        // verify that Property attributes match
        delta.verifyMatch(base, true, errlist);

        // if the base is resolved, this will be resolved when resolve
        // completes (otherwise it will be a derivation/modification)
        int nBaseMode  = base .getMode();
        int nDeltaMode = delta.getMode();
        boolean fBaseResolved = (nBaseMode == RESOLVED);

        // attributes represented as bit flags
        int nBaseFlags    = base .m_nFlags;
        int nDeltaFlags   = delta.m_nFlags;
        int nDerivedFlags = nBaseFlags;

        // resolve exists
        int nExists = EXISTS_UPDATE;

        // if this is a modification of an insert, then keep it as an insert
        if (nDeltaMode == MODIFICATION && (nBaseFlags & EXISTS_MASK) == EXISTS_INSERT)
            {
            nExists = EXISTS_INSERT;
            }
        nDerivedFlags = nDerivedFlags & ~EXISTS_FULLMASK | nExists;

        // the access, static, final, persistent, indexed, and direction
        // attributes are only set at the declaration level (but are stored
        // redundantly at every level in order to determine if changes have
        // occurred at a base level)
        // (ACCESS, SCOPE, DERIVE, STG, PROP, DIR)

        // the visibility attribute is flexible
        if ((nDeltaFlags & VIS_SPECIFIED) != 0)
            {
            nDerivedFlags = nDerivedFlags & ~VIS_FULLMASK | nDeltaFlags & VIS_FULLMASK;
            }

        // the antiquity (deprecated) attribute is flexible
        if ((nDeltaFlags & ANTIQ_SPECIFIED) != 0)
            {
            nDerivedFlags = nDerivedFlags & ~ANTIQ_FULLMASK | nDeltaFlags & ANTIQ_FULLMASK;
            }

        // Property data type
        DataType dtBase  = base .m_dt;
        DataType dtDelta = delta.m_dt;

        // store Property attributes
        derived.m_sName  = base.m_sName;
        derived.m_dt     = dtBase;
        derived.m_nFlags = nDerivedFlags;

        // Property value information
        Object   oValue         = delta.m_oValue;
        boolean  fSizeDelta     = delta.m_fSizeDelta;
        Object   oPrevValue     = delta.m_oPrevValue;
        boolean  fPrevSizeDelta = delta.m_fPrevSizeDelta;

        // determine if a change to certain attributes of the Property has
        // invalidated the value attribute of the Property
        boolean fTypeChange = dtDelta != dtBase &&
                             !(   isDataTypeEnumNumeric(getDataTypeEnum(dtDelta))
                               && isDataTypeEnumNumeric(getDataTypeEnum(dtBase )) );
        boolean fPropChange = ((nDeltaFlags ^ nBaseFlags) & PROP_MASK) != 0 &&
                              (   (nDeltaFlags & PROP_MASK) == PROP_SINGLE
                               || (nBaseFlags  & PROP_MASK) == PROP_SINGLE );
        boolean fFinalBase  = (nDeltaMode != MODIFICATION) &&
                              (nBaseFlags & DERIVE_MASK) == DERIVE_FINAL;
        boolean fNoSetter   = fBaseResolved && !derived.isValueSettable();
        if (fTypeChange || fPropChange || fFinalBase || fNoSetter)
            {
            oValue         = NO_DELTA;
            fSizeDelta     = false;
            oPrevValue     = NO_DELTA;
            fPrevSizeDelta = false;
            }

        // resolve the default ("previous") Property value
        Object oBaseValue = base.m_oValue;
        if (base.getProcessState() != STATE_RESOLVED)
            {
            oBaseValue = base.resolveValue((nBaseFlags & PROP_MASK) == PROP_SINGLE,
                    base.m_fSizeDelta, base.m_oPrevValue, oBaseValue, false, loader, errlist);
            }
        oPrevValue = derived.resolveValue((nBaseFlags & PROP_MASK) == PROP_SINGLE,
                fPrevSizeDelta, oBaseValue, oPrevValue, false, loader, errlist);
        fPrevSizeDelta = fPrevSizeDelta || base.m_fSizeDelta || base.m_fPrevSizeDelta;

        // store Property value information
        derived.m_fSizeDelta     = fSizeDelta;
        derived.m_oValue         = oValue;
        derived.m_fPrevSizeDelta = fPrevSizeDelta;
        derived.m_oPrevValue     = oPrevValue;

        return derived;
        }

    /**
    * Complete the resolve processing for this Property and its sub-traits.
    *
    * @param loader    the Loader object for JCS, CIM, and CD dependencies
    * @param errlist   the error list object to log error information to
    *
    * @exception ComponentException  thrown only if a fatal error occurs
    */
    protected synchronized void finalizeResolve(Loader loader, ErrorList errlist)
            throws ComponentException
        {
        super.finalizeResolve(loader, errlist);

        // remove all "specified" flags
        m_nFlags &= ~ALL_SPECIFIED;

        // add Property origin to the Behaviors if the Property is added at
        // this level
        if (isDeclaredAtThisLevel() && !isConstant())
            {
            Behavior[] abeh = getAccessors();
            int        cbeh = abeh.length;
            for (int i = 0; i < cbeh; ++i)
                {
                Behavior behavior = abeh[i];

                // assertion:  verify that all necessary accessors exist
                if (behavior == null
                    && isAccessorApplicable(i)
                    && !getComponent().isSignature())
                    {
                    throw new IllegalStateException(CLASS + ".finalizeResolve:  "
                            + "Missing accessor!  (property " + m_sName
                            + ", accessor " + i + ")");
                    }

                if (behavior != null)
                    {
                    behavior.addOriginTrait(this);
                    }
                }
            }

        // if necessary set accessor is unaccessible for in/inout properties
        // then reset the value to its default (prev)
        if (!isValueSettable()
            // gg: 2001.8.22 since values are not settable for Signatures
            // we need to neutralize the effect (see isValueSettable)
            && !getComponent().isSignature())
            {
            m_oValue     = NO_DELTA;
            m_fSizeDelta = false;
            }

        // final-resolve the Property values (default and current)
        boolean fSingle = isSingle();
        m_oPrevValue = resolveValue(fSingle, false, m_oPrevValue,
                NO_DELTA, true, loader, errlist);
        m_oValue = resolveValue(fSingle, m_fSizeDelta, m_oPrevValue,
                m_oValue, true, loader, errlist);

        m_fSizeDelta     = false;
        m_fPrevSizeDelta = false;
        }

    /**
    * Apply the derivation/modification value to the "base" value.
    *
    * @param fSingle
    * @param fSizeDelta
    * @param oBase
    * @param oThis
    * @param fFinal
    * @param loader
    * @param errlist
    *
    * @return the resolved value
    *
    * @exception ComponentException  thrown only if a fatal error occurs
    */
    private Object resolveValue(boolean fSingle, boolean fSizeDelta, Object oBase, Object oThis, boolean fFinal, Loader loader, ErrorList errlist)
            throws ComponentException
        {
        // check if the new value is no change
        if (oThis == NO_DELTA)
            {
            // the new value is no change from the base,
            // return the base along
            if (oBase == NO_DELTA)
                {
                // the base is no change also, if this is the
                // final resolve, return no value
                if (fFinal)
                    {
                    oBase = NO_VALUE;
                    }
                return oBase;
                }

            // check if the base is any of the other special values
            if (oBase == NO_VALUE || oBase == null)
                {
                return oBase;
                }

            // we have an actual value to deal with, check if we
            // need to worry about making a copy of the value
            if (isComplex())
                {
                if (fSingle)
                    {
                    oBase = resolveComplexValue(NO_DELTA, oBase, fFinal, loader, errlist);
                    }
                else
                    {
                    // create a new version of the array of values
                    oBase = ((Object[]) oBase).clone();
                    Object[] aoBase = (Object[]) oBase;
                    for (int i = 0; i < aoBase.length; ++i)
                        {
                        Object oElement = aoBase[i];
                        if (oElement != NO_DELTA && oElement != NO_VALUE && oElement != null)
                            {
                            aoBase[i] = resolveComplexValue(NO_DELTA, oElement, fFinal, loader, errlist);
                            }
                        }
                    }
                }

            // pass the base value along
            return oBase;
            }

        // check if the new value is any of the other special values
        // that override the base to matter what
        if (oThis == NO_VALUE || oThis == null)
            {
            return oThis;
            }

        // we have an actual value specified for the new value, we still
        // need to deal with merging any sub-values (either as an array or
        // as a complex property)

        if (fSingle)
            {
            if (isComplex())
                {
                oThis = resolveComplexValue(oBase, oThis, fFinal, loader, errlist);
                }
            return oThis;
            }

        // merge the indexed value arrays

        Object[] aoThis   = (Object[]) oThis;
        int      cThis    = aoThis.length;

        boolean  fBaseIsArray = oBase instanceof Object[];
        Object[] aoBase   = (fBaseIsArray ? (Object[]) oBase : NO_OBJECTS);
        int      cBase    = aoBase.length;

        int      cResult  = (fSizeDelta || !fBaseIsArray ? cThis : cBase);
        Object[] aoResult = new Object[cResult];
        for (int i = 0; i < cResult; ++i)
            {
            oThis = (i < cThis ? aoThis[i] : NO_DELTA);
            oBase = (i < cBase ? aoBase[i] : NO_DELTA);
            aoResult[i] = resolveValue(true, false, oBase, oThis, fFinal, loader, errlist);
            }

        return aoResult;
        }

    /**
    * Resolve the complex property value.  It is assumed that the oThis parameter
    * has already been checked for the NO_DELTA, NO_VALUE, and null values.
    *
    * @param oBase
    * @param oThis
    * @param fFinal
    * @param loader
    * @param errlist
    *
    * @return the resolved complex property value
    *
    * @exception ComponentException  thrown only if a fatal error occurs
    */
    private Object resolveComplexValue(Object oBase, Object oThis, boolean fFinal, Loader loader, ErrorList errlist)
            throws ComponentException
        {
        Component cdThis    = (Component) oThis;
        // flag if cdThis is still the originally passed in component
        boolean   fOriginal = true;

        // check if the base is not a special value
        if (oBase != NO_DELTA && oBase != NO_VALUE && oBase != null)
            {
            // if the new complex property is of the same type
            // as the base complex property, resolve the new value
            // against the base value
            Component cdBase = (Component) oBase;
            if (cdThis.getSuperName().equals(cdBase.getSuperName()))
                {
                cdThis    = (Component) cdBase.resolve(cdThis, this, loader, errlist);
                fOriginal = false;
                }
            }

        // if this is the final resolve and the complex property
        // has not been fully resolved, load it's base component
        // definition to resolve this complex property against
        if (fFinal && cdThis.getMode() != RESOLVED)
            {
            Component cdBase = loader.loadComponent(cdThis.getSuperName(), true, errlist);
            if (cdBase == null)
                {
                logError(RESOLVE_PROPVALUEORPHANED, WARNING, new Object[]
                        {cdThis.toString(), toPathString()}, errlist);
                return NO_VALUE;
                }
            cdThis    = (Component) cdBase.resolve(cdThis, this, loader, errlist);
            fOriginal = false;
            }

        // if we still have the original passed in component, create
        // a new version with ourselves as it's parent
        if (fOriginal)
            {
            cdThis = new Component(this, cdThis);
            }

        // and finally, finalize the resolve if this is final
        if (fFinal)
            {
            cdThis.finalizeResolve(loader, errlist);

            if (cdThis.isDiscardable())
                {
                cdThis.invalidate();
                return NO_VALUE;
                }
            }

        return cdThis;
        }

    /**
    * Create a derivation/modification Trait by determining the differences
    * between the derived Trait ("this") and the passed base Trait.  Neither
    * the derived Trait ("this") nor the base may be modified in any way.
    *
    * @param traitBase  the base Trait to extract
    * @param parent     the Trait which will contain the resulting Trait or
    *                   null if the resulting Trait will not be contained
    * @param loader     the Loader object for JCS, CIM, and CD dependencies
    * @param errlist    the error list object to log error information to
    *
    * @return the delta between this derived Trait and the base Trait
    *
    * @exception ComponentException  thrown only if a fatal error occurs
    */
    protected Trait extract(Trait traitBase, Trait parent, Loader loader, ErrorList errlist)
            throws ComponentException
        {
        Property derived = this;
        Property base    = (Property) traitBase;
        Property delta   = (Property) super.extract(base, parent, loader, errlist);

        // verify that Property attributes match
        derived.verifyMatch(base, false, errlist);

        // determine if the base is resolved (which means all base attributes
        // are specified, i.e. have a value)
        boolean fBaseResolved = (base.getMode() == RESOLVED);

        // attributes represented as bit flags; see the note in the
        // corresponding section of the extract() method in Component.java
        int nBaseFlags    = base   .m_nFlags;
        int nDerivedFlags = derived.m_nFlags;
        int nDifFlags     = nBaseFlags ^ nDerivedFlags;
        int nDeltaFlags   = nDerivedFlags & ~ALL_SPECIFIED;

        // extract exists:  always an update
        nDeltaFlags = nDeltaFlags & ~EXISTS_FULLMASK | EXISTS_UPDATE;

        // the access, static, final, persistent, indexed, and direction
        // attributes are only set at the declaration level (but are stored
        // redundantly at every level in order to determine if changes have
        // occurred at a base level)
        // (ACCESS, SCOPE, DERIVE, STG, PROP, DIR)
        nDeltaFlags = nDeltaFlags & ~DECL_ONLY_MASK | nBaseFlags & DECL_ONLY_MASK;

        // visible
        if ((nDifFlags & VIS_MASK) != 0 &&
                (fBaseResolved || (nBaseFlags & VIS_SPECIFIED) != 0))
            {
            nDeltaFlags |= VIS_SPECIFIED;
            }

        // antiquity
        if ((nDifFlags & ANTIQ_MASK) != 0 &&
                (fBaseResolved || (nBaseFlags & ANTIQ_SPECIFIED) != 0))
            {
            nDeltaFlags |= ANTIQ_SPECIFIED;
            }

        // Property data type
        DataType dtBase    = base   .m_dt;
        DataType dtDerived = derived.m_dt;

        // store Property attributes
        delta.m_sName  = base.m_sName;
        delta.m_dt     = dtBase;
        delta.m_nFlags = nDeltaFlags;

        // Property value information
        Object  oValue     = derived.m_oValue;
        Object  oPrevValue = derived.m_oPrevValue;

        // check if this is the first extract performed
        // against this property
        if (derived.getProcessState() == STATE_RESOLVED)
            {
            // arrays are mutable; make a copy for the delta
            if (oValue instanceof Object[])
                {
                oValue = ((Object[]) oValue).clone();
                }

            // calculate the changes made from the previous value
            // determined while this property was resolved
            delta.m_oValue     = oValue;
            delta.m_oPrevValue = oPrevValue;
            delta.initializeExtract(loader, errlist);
            oValue             = delta.m_oValue;

            // recalculate the previous value
            // while extracting
            oPrevValue = NO_DELTA;
            }

        // if there is a delta value to worry about
        if (oValue != NO_DELTA)
            {
            // determine if a change to certain attributes of the Property has
            // invalidated the value attribute of the Property
            boolean fTypeChange = dtDerived != dtBase &&
                                 !(   isDataTypeEnumNumeric(getDataTypeEnum(dtDerived))
                                   && isDataTypeEnumNumeric(getDataTypeEnum(dtBase   )) );
            boolean fPropChange = (nDifFlags & PROP_MASK) != 0 &&
                                  (   (nDerivedFlags & PROP_MASK) == PROP_SINGLE
                                   || (nBaseFlags    & PROP_MASK) == PROP_SINGLE );
            boolean fFinalBase  = (delta.getMode() != MODIFICATION) &&
                                  (nBaseFlags & DERIVE_MASK) == DERIVE_FINAL;
            boolean fNoSetter   = fBaseResolved && !derived.isValueSettable();
            if (fTypeChange || fPropChange || fFinalBase || fNoSetter)
                {
                oValue         = NO_DELTA;
                oPrevValue     = NO_DELTA;
                }
            else
                {
                // add the base modification to the "previous" value
                boolean fSingle   = isSingle();
                Object oBaseValue = base.m_oValue;
                if (base.getProcessState() != STATE_RESOLVED)
                    {
                    oBaseValue = base.resolveValue(fSingle, base.m_fSizeDelta,
                            base.m_oPrevValue, oBaseValue, false, loader, errlist);
                    }
                oPrevValue = delta.resolveValue(fSingle, base.m_fSizeDelta || base.m_fPrevSizeDelta,
                        oPrevValue, oBaseValue, false, loader, errlist);
                }
            }

        // store Property value information
        delta.m_oValue     = oValue;
        delta.m_oPrevValue = oPrevValue;

        return delta;
        }

    /**
    * Initialized the extraction of the Property trait.  This call is performed by
    * the Component at the first extract level if there was not a base Property
    * to extract from.  This is required to allow extraction of the base Component
    * of any complex property values.
    *
    * @param loader    the Loader object for JCS, CIM, and CD dependencies
    * @param errlist   the error list object to log error information to
    */
    protected void initializeExtract(Loader loader, ErrorList errlist)
            throws ComponentException
        {
        // at each extract level, the "previous" value has been resolved;
        // the actual Property value to store is the delta between that
        // "previous" value and the current Property value
        extractPreviousValue(loader, errlist);

        // if we have a complex property value, initialize it
        Object oValue = m_oValue;
        if (isComplex() && oValue != NO_DELTA && oValue != NO_VALUE && oValue != null)
            {
            if (isSingle())
                {
                m_oValue = initializeExtractComplexValue(oValue, loader, errlist);
                }
            else
                {
                Object[] aoValue = (Object[]) oValue;
                for (int i = 0; i < aoValue.length; ++i)
                    {
                    Object oElement = aoValue[i];
                    if (oElement != NO_DELTA && oElement != NO_VALUE && oElement != null)
                        {
                        aoValue[i] = initializeExtractComplexValue(oElement, loader, errlist);
                        }
                    }
                }
            }
        }

    /**
    * Initialize the extract process for a complex property value.
    *
    * @param oValue the complex property value to use
    * @param loader    the Loader object for JCS, CIM, and CD dependencies
    * @param errlist   the error list object to log error information to
    *
    * @return the initialized complex property value
    */
    private Object initializeExtractComplexValue(Object oValue, Loader loader, ErrorList errlist)
            throws ComponentException
        {
        Component cdValue = (Component) oValue;
        if (cdValue.getMode() != RESOLVED)
            {
            return cdValue;
            }

        Component cdBase = loader.loadComponent(cdValue.getSuperName(), true, errlist);
        if (cdBase == null)
            {
            logError(EXTRACT_PROPVALUEORPHANED, WARNING, new Object[]
                    {cdValue.toString(), toPathString()}, errlist);
            cdValue.invalidate();
            return NO_DELTA;
            }

        return cdValue.extract(cdBase, this, loader, errlist);
        }

    /**
    * Complete the extract processing for this Property and its sub-traits.
    *
    * @param loader    the Loader object for JCS, CIM, and CD dependencies
    * @param errlist   the error list object to log error information to
    *
    * @exception ComponentException  thrown only if a fatal error occurs
    */
    protected synchronized void finalizeExtract(Loader loader, ErrorList errlist)
            throws ComponentException
        {
        super.finalizeExtract(loader, errlist);

        // at each extract level, the "previous" value has been resolved;
        // the actual Property value to store is the delta between that
        // "previous" value and the current Property value
        extractPreviousValue(loader, errlist);

        // if we have complex property values, finalize the extraction
        // of the complex properties
        Object oValue = m_oValue;
        if (isComplex() && oValue != NO_DELTA && oValue != NO_VALUE && oValue != null)
            {
            if (isSingle())
                {
                if (finalizeExtractComplexValue(oValue, loader, errlist))
                    {
                    m_oValue = NO_DELTA;
                    }
                }
            else
                {
                // find all complex property values in the indexed array
                Object[] aoValue = (Object[]) m_oValue;
                boolean  fDelta  = false;
                for (int i = 0; i < aoValue.length; ++i)
                    {
                    Object oElement = aoValue[i];
                    if (oElement == NO_DELTA)
                        {
                        continue;
                        }
                    if (oElement != NO_VALUE && oElement != null)
                        {
                        if (finalizeExtractComplexValue(oElement, loader, errlist))
                            {
                            aoValue[i] = NO_DELTA;
                            continue;
                            }
                        }
                    fDelta = true;
                    }
                // if all complex properties were discarded, then we
                // have no overall value delta
                if (!fDelta && !m_fSizeDelta)
                    {
                    m_oValue = NO_DELTA;
                    }
                }
            }

        // Property default value is always set to "no delta" by extract
        m_oPrevValue = NO_DELTA;
        }

    /**
    * Finalize the extraction of a complex property value
    *
    * @param oValue the complex property value to finalize
    * @param loader    the Loader object for JCS, CIM, and CD dependencies
    * @param errlist   the error list object to log error information to
    *
    * @return true if the complex property was discarded
    */
    private boolean finalizeExtractComplexValue(Object oValue, Loader loader, ErrorList errlist)
            throws ComponentException
        {
        Component cdValue = (Component) oValue;
        cdValue.finalizeExtract(loader, errlist);

        // check if the value is discardable
        if (cdValue.isDiscardable())
            {
            cdValue.invalidate();
            return true;
            }

        return false;
        }

    /**
    * Extract a derivation/modification value by extracting out the
    * previous value from the current value.
    *
    * @param loader
    * @param errlist
    *
    * @exception ComponentException  thrown only if a fatal error occurs
    */
    private void extractPreviousValue(Loader loader, ErrorList errlist)
            throws ComponentException
        {
        // first check if the value is already no delta
        Object oValue = m_oValue;
        if (oValue == NO_DELTA)
            {
            return;
            }

        // next check if they are the same special type
        Object oPrevValue = m_oPrevValue;
        if (oValue == oPrevValue)
            {
            m_oValue = NO_DELTA;
            return;
            }

        // next if the new value is a special type and it is
        // *not* equal to the previous value, then use the
        // new value
        if (oValue == NO_VALUE || oValue == null)
            {
            return;
            }

        // get the data type and the single/indexed status
        DataType dt       = m_dt;
        boolean  fComplex = isComplex();

        // if we are a single value
        if (isSingle())
            {
            // check if our values are equal
            if (oValue == oPrevValue
                || isValueEqual(dt, oValue, oPrevValue))
                {
                m_oValue = NO_DELTA;
                }
            else
                {
                if (fComplex)
                    {
                    m_oValue = extractPreviousComplexValue(oPrevValue, oValue, loader, errlist);
                    }
                }
            return;
            }

        Object[] aoValue     = (Object[]) oValue;
        Object[] aoPrevValue = (oPrevValue instanceof Object[] ? (Object[]) oPrevValue : NO_OBJECTS);
        int cValue     = aoValue.length;
        int cPrevValue = aoPrevValue.length;

        int     cElement = Math.min(cValue, cPrevValue);
        boolean fDelta   = false;
        for (int i = 0; i < cElement; ++i)
            {
            Object oElement = aoValue[i];
            if (oElement != NO_DELTA)
                {
                Object oPrevElement = aoPrevValue[i];
                if (oElement == oPrevElement
                    || isValueEqual(dt, oElement, oPrevElement))
                    {
                    oElement = NO_DELTA;
                    }
                else
                    {
                    if (fComplex && oElement != NO_DELTA && oElement != NO_VALUE && oElement != null)
                        {
                        oElement = extractPreviousComplexValue(oPrevElement, oElement, loader, errlist);
                        }
                    }
                aoValue[i] = oElement;
                if (oElement != NO_DELTA)
                    {
                    fDelta = true;
                    }
                }
            }

        m_fSizeDelta = (cValue != cPrevValue);
        if (!fDelta && !m_fSizeDelta)
            {
            m_oValue = NO_DELTA;
            }
        }

    /**
    * Extract a previous value from a complex property value.  It is
    * assumed that the value is a complex property.
    *
    * @param oPrevValue the previos value to extract from the current value
    * @param oValue the complex property value to extract from
    * @param loader    the Loader object for JCS, CIM, and CD dependencies
    * @param errlist   the error list object to log error information to
    *
    * @return the resulting complex property value
    */
    private Object extractPreviousComplexValue(Object oPrevValue, Object oValue, Loader loader, ErrorList errlist)
            throws ComponentException
        {
        Component cdValue = (Component) oValue;
        if (oPrevValue != NO_DELTA && oPrevValue != NO_VALUE && oPrevValue != null)
            {
            Component cdPrevValue = (Component) oPrevValue;
            if (cdValue.getSuperName().equals(cdPrevValue.getSuperName()))
                {
                cdValue = (Component) cdValue.extract((Component) oPrevValue, this, loader, errlist);
                }
            }

        if (cdValue.isDiscardable())
            {
            cdValue.invalidate();
            return NO_DELTA;
            }

        return cdValue;
        }

    /**
    * Check for illegal mismatches between the base and this Property.
    *
    * (Neither the base nor this Property are modified by this method.)
    *
    * @param base      the super or base level's Property object
    * @param fResolve  true if being RESOLVED
    * @param errlist   the error list to log any mismatches to
    *
    * @exception DerivationException
    */
    protected void verifyMatch(Property base, boolean fResolve, ErrorList errlist)
            throws DerivationException
        {
        // check for name mismatch
        if (!this.m_sName.equals(base.m_sName))
            {
            String sCode = (fResolve ? RESOLVE_PROPNAMECHANGE
                                     : EXTRACT_PROPNAMECHANGE);
            Object[] aoParam = new Object[]
                {
                this.m_sName,
                base.m_sName,
                toPathString()
                };
            logError(sCode, WARNING, aoParam, errlist);
            }

        // check for data type mismatch if not a Signature
        if (!getComponent().isSignature())
            {
            if (this.m_dt != base.m_dt)
                {
                String sCode = (fResolve ? RESOLVE_PROPTYPECHANGE
                                         : EXTRACT_PROPTYPECHANGE);
                Object[] aoParam = new Object[]
                    {
                    base.m_sName,
                    this.m_dt.toString(),
                    base.m_dt.toString(),
                    toPathString()
                    };
                logError(sCode, WARNING, aoParam, errlist);
                }
            }
        }

    /**
    * Determine if this Property will be derived by a sub-Component.
    * Private constants are not derived.  Properties with only private
    * accessors are not derived.  All other Properties are derived.
    *
    * @return true if the Property will be derived by a sub-Component
    */
    protected boolean isDerivedBySub()
        {
        if (isFromSuper())
            {
            return true;
            }

        if (isConstant())
            {
            return getAccess() != ACCESS_PRIVATE;
            }

        Behavior[] aBehavior = getAccessors();
        int        cBehavior = aBehavior.length;
        for (int i = 0; i < cBehavior; ++i)
            {
            Behavior behavior = aBehavior[i];
            if (behavior != null && behavior.getAccess() != ACCESS_PRIVATE)
                {
                return true;
                }
            }

        return false;
        }

    /**
    * Determines if this Property can be discarded.
    *
    * @return true if the Property has no reason to exist
    */
    protected boolean isDiscardable()
        {
        switch (getMode())
            {
            case RESOLVED:
                {
                // Don't discard Java Class Signature Properties
                if (getComponent().isSignature())
                    {
                    return false;
                    }
                break;
                }
            case DERIVATION:
            case MODIFICATION:
                {
                // check for delta information
                if ((m_nFlags & ALL_SPECIFIED) != 0 || m_fSizeDelta || m_oValue != NO_DELTA)
                    {
                    return false;
                    }
                break;
                }
            }

        return super.isDiscardable();
        }

    /**
    * Determine whether this trait contains information that would cause
    * generation of a class that would operate differently than the class
    * generated from the information maintained by the super trait.
    *
    * @param base     the base (i.e. the super) Trait to compare to
    *
    * @return true if a delta between this trait and its super trait means
    *         that class generation should generate the class corresponding
    *         to this trait, otherwise false
    */
    protected boolean isClassDiscardable(Trait base)
        {
        Property that = (Property) base;
        if (that == null || !isFromSuper())
            {
            return false;
            }

        // no property attributes can be overridden
        if (((this.m_nFlags ^ that.m_nFlags) & CLASSGEN_FLAGS) != 0)
            {
            return false;
            }

        // value cannot change
        if (!this.isValueEqual(that.m_oValue))
            {
            return false;
            }

        return super.isClassDiscardable(base);
        }


    // ----- miscellaneous Trait methods ------------------------------------

    /**
    * Determine if the Property is modifiable.
    *
    * Java constants are only modifiable at their declaration level or
    * modifications at that derivation level.
    *
    * @return true if the Property is modifiable, false otherwise
    */
    public boolean isModifiable()
        {
        if (isStatic() && isFromSuper())
            {
            return false;
            }

        return super.isModifiable();
        }

    /**
    * Determine all traits contained by this behavior.
    *
    * @return the an enumeration of traits contained by this trait
    */
    protected Enumeration getSubTraits()
        {
        if (isComplex())
            {
            Object oValue = m_oValue;
            if (isSingle())
                {
                if (oValue instanceof Component)
                    {
                    return new SimpleEnumerator(new Object[] {oValue});
                    }
                }
            else
                {
                if (oValue instanceof Object[])
                    {
                    Object[] aoValue = (Object[]) oValue;
                    LiteSet set = null;
                    for (int i = 0; i < aoValue.length; ++i)
                        {
                        oValue = aoValue[i];
                        if (oValue instanceof Component)
                            {
                            if (set == null)
                                {
                                set = new LiteSet();
                                }
                            set.add(oValue);
                            }
                        }
                    if (set != null)
                        {
                        return set.elements();
                        }
                    }
                }
            }

        return NullImplementation.getEnumeration();
        }

    /**
    * Reset state, discarding all information.
    */
    protected synchronized void invalidate()
        {
        super.invalidate();

        m_sName      = null;
        m_dt         = null;
        m_oValue     = null;
        m_oPrevValue = null;
        }

    /**
    * The Property's name is its unique string identifier.
    *
    * @return the Property name
    */
    protected String getUniqueName()
        {
        return m_sName;
        }

    /**
    * Determine the unique description for this trait.
    *
    * All traits must be uniquely identifiable by a "description string".
    * This enables the origin implementation built into Trait to use the
    * description string to differentiate between Trait origins.
    *
    * @return the description string for the trait
    */
    protected String getUniqueDescription()
        {
        return DESCRIPTOR + ' ' + getUniqueName();
        }


    // ----- accessors ------------------------------------------------------

    /**
    * Determine the Component Definition which contains this property.
    * Properties are always contained by a Component Definition.
    *
    * @return the Component Definition instance that contains this property
    */
    public Component getComponent()
        {
        return (Component) getParentTrait();
        }

    /**
    * Determine if this Property is a field of a complex property value.
    *
    * @return true if part of a complex property
    */
    public boolean isComponentComplex()
        {
        return getComponent().isComplex();
        }


    // ----- categorization (helpers)

    /**
    * A constant is a Property that is "persistent out".
    *
    * @return true if this Property is categorized as a constant Property,
    *         whether a Java constant or a virtual constant
    */
    public boolean isConstant()
        {
        return ((STG_MASK    | DIR_MASK) & m_nFlags) ==
                (STG_PERSIST | DIR_OUT );
        }

    /**
    * A Java constant is a Property that is "persistent final out".  Java
    * constant Properties produce final Java fields in the resulting class
    * structures.
    *
    * @return true if this Property is categorized as a Java constant
    */
    public boolean isJavaConstant()
        {
        return ((STG_MASK    | DERIVE_MASK  | DIR_MASK) & m_nFlags) ==
                (STG_PERSIST | DERIVE_FINAL | DIR_OUT );
        }

    /**
    * A virtual constant is a Property that is "persistent [instance]
    * derivable out".
    *
    * @return true if this Property is categorized as a virtual constant
    */
    public boolean isVirtualConstant()
        {
        return ((STG_MASK    | DERIVE_MASK      | DIR_MASK) & m_nFlags) ==
                (STG_PERSIST | DERIVE_DERIVABLE | DIR_OUT );
        }

    /**
    * A design-only Property is a Property that is used only by design tools.
    *
    * Currently it is a synthetic (secondary) feature that is defined as
    * "hidden protected virtual property whose name starts with '$'"
    * and is inteneded to be used by specialized design tools
    *
    * @return true if this Property is categorized as a design property
    */
    public boolean isDesignOnly()
        {
        return isVirtualConstant()
            && getVisible()      == VIS_HIDDEN
            && getAccess()       == ACCESS_PROTECTED
            && m_sName.charAt(0) == '$';
        }

    /**
    * A functional Property is declared as "in".
    *
    * @return true if this Property is categorized as a functional Property
    */
    public boolean isFunctionalProperty()
        {
        return (DIR_MASK & m_nFlags) == DIR_IN;
        }

    /**
    * A calculated Property is declared as "transient out".
    *
    * @return true if this Property is categorized as a calculated Property
    */
    public boolean isCalculatedProperty()
        {
        return ((STG_MASK      | DIR_MASK) & m_nFlags) ==
                (STG_TRANSIENT | DIR_OUT );
        }

    /**
    * A standard Property is declared as "inout".
    *
    * @return true if this Property is categorized as a standard Property
    */
    public boolean isStandardProperty()
        {
        return (DIR_MASK & m_nFlags) == DIR_INOUT;
        }

    /**
    * A complex property property is a property which can be used by
    * a complex property component.  Component resolve uses this method
    * to decide which properties of the base Component should be moved
    * across to the complex property component.
    *
    * @return true if this Property is categorized as a standard Property
    */
    protected boolean isComplexPropertyProperty()
        {
        // indexed only properties do not have a settable single value
        if (getIndexed() == PROP_INDEXEDONLY)
            {
            return false;
            }

        // arrays of complex property values are not currently supported
        DataType dt = m_dt;
        if (dt.isArray()
            && dt.getBaseElementType().isComponent())
            {
            return false;
            }

        // a complex property property must have a public setter
        Behavior bhvr = getApplicableAccessor(isSingle() ?
                Property.PA_SET_SINGLE : Property.PA_SET_ARRAY);
        if (bhvr == null || bhvr.getAccess() != ACCESS_PUBLIC)
            {
            return false;
            }

        return true;
        }

    // ----- reserved behaviors (helpers)

    /**
    * Get all the existing Behaviors reserved by this Property.
    *
    * @return an array of seven Behaviors, any of which may be null, indexed
    *         by the RSVD_ (reserved Property Accessors) constants.
    */
    public Behavior[] getReservedBehaviors()
        {
        return getReservedBehaviors(getComponent(), m_dt, m_sName);
        }

    /**
    * Get all the Behaviors reserved to this Property assuming the passed
    * DataType and name.  (This allows for "what if" scenarios if the type
    * and/or name would be changed.)
    *
    * @param dt     the DataType of the Property
    * @param sName  the name of the Property
    *
    * @return an array of seven Behaviors, any of which may be null, indexed
    *         by the RSVD_ (reserved Property Accessors) constants
    */
    protected Behavior[] getReservedBehaviors(DataType dt, String sName)
        {
        return getReservedBehaviors(getComponent(), dt, sName);
        }

    /**
    * Get all the Behaviors reserved to this Property assuming the passed
    * DataType and name.  (This allows for "what if" scenarios if the type
    * and/or name would be changed.)
    *
    * @param cd     the containing Component
    * @param dt     the DataType of the Property
    * @param sName  the name of the Property
    *
    * @return an array of seven Behaviors, any of which may be null, indexed
    *         by the RSVD_ (reserved Property Accessors) constants
    */
    protected static Behavior[] getReservedBehaviors(Component cd, DataType dt, String sName)
        {
        String[]    asBehavior  = getReservedBehaviorSignatures(dt, sName);
        int         cBehavior   = asBehavior.length;
        Behavior[]  aBehavior   = new Behavior[cBehavior];

        // search for all related behaviors
        for (int i = 0; i < cBehavior; ++i)
            {
            aBehavior[i] = cd.getBehavior(asBehavior[i]);
            }

        return aBehavior;
        }

    /**
    * Get all the Behavior signatures that are reserved by this Property
    * assuming the passed DataType and name.
    *
    * @param dt     the DataType of the Property
    * @param sName  the name of the Property
    *
    * @return an array of seven Behavior signatures, indexed by the RSVD_
    *         (reserved Property Accessors) constants
    */
    protected static String[] getReservedBehaviorSignatures(DataType dt, String sName)
        {
        // build a list of all possibly reserved Behaviors
        String sType = dt.getTypeString();
        String sSet  = "set" + sName;
        String sGet  = "get" + sName;
        String sIs   = "is"  + sName;

        return new String[]
            {
            sSet + '(' + sType + ')',   // setN(T)      single setter
            sSet + "([" + sType + ')',  // setN(T[])    array setter
            sSet + "(I" + sType + ')',  // setN(int, T) index setter
            sGet + "()",                // getN()       single or array getter
            sGet + "(I)",               // getN(int)    index getter
            sIs  + "()",                // isN()        boolean single or array getter
            sIs  + "(I)",               // isN(int)     boolean index getter
            };
        }

    /**
    * Determine if the specified Behaviors signature is reserved by this
    * Property.
    *
    * @param sSig  the signature of the Behavior
    *
    * @return true if this Property reserves the specified Behavior signature
    */
    public boolean isReservedBehaviorSignature(String sSig)
        {
        final Collator INSENS = Constants.INSENS;

        // is the signature an accessor at all?
        String sPrefix = getAccessorPrefix(sSig);
        if (sPrefix == null)
            {
            return false;
            }

        // is the signature possibly an accessor for this property?
        String sName = m_sName;
        if (sName == null || !INSENS.equals(sName, getPropertyName(sSig)))
            {
            return false;
            }

        // rebuild the signature (in case of case conflicts in the original)
        sSig = Behavior.getSignature(sName, Behavior.getParameterTypes(sSig), 1);

        // get all reserved signatures for this property
        String[] asSigs = getReservedBehaviorSignatures(m_dt, m_sName);

        // is the specified signature reserved by this property?
        switch (sPrefix.charAt(0))
            {
            case 's':   // set
                return sSig.equals(asSigs[0]) ||
                       sSig.equals(asSigs[1]) ||
                       sSig.equals(asSigs[2]);
            case 'g':   // get
                return sSig.equals(asSigs[3]) ||
                       sSig.equals(asSigs[4]);
            case 'i':   // is
                return sSig.equals(asSigs[5]) ||
                       sSig.equals(asSigs[6]);
            default:
                // assert
                throw new IllegalStateException();
            }
        }


    // ----- parsing behavior signatures (helpers)

    /**
    * Determine if a signature is prefixed as a property accessor, and if so,
    * what accessor prefix is used.
    *
    * @param sSig  a behavior signature
    *
    * @return the property accessor prefix, or null if the signature does not
    *         follow the design pattern of an accessor
    */
    public static String getAccessorPrefix(String sSig)
        {
        final Collator INSENS  = Constants.INSENS;
        final String   GETTER  = "get";
        final String   SETTER  = "set";
        final String   BOOLGET = "is";

        if (sSig.length() > 3)
            {
            // check for getter
            String sPrefix = sSig.substring(0, 3);
            if (INSENS.equals(sPrefix, GETTER))
                {
                return GETTER;
                }

            // check for setter
            if (INSENS.equals(sPrefix, SETTER))
                {
                return SETTER;
                }
            }

        if (sSig.length() > 2)
            {
            // check for boolean getter
            if (INSENS.equals(sSig.substring(0, 2), BOOLGET))
                {
                return BOOLGET;
                }
            }

        return null;
        }

    /**
    * Determine if a signature is named as a property accessor, and if so,
    * what the name of the Property is.
    *
    * @param sSig  a behavior signature
    *
    * @return the property name, or null if the signature does not follow
    *         the design pattern of an accessor
    */
    public static String getPropertyName(String sSig)
        {
        String sPrefix = getAccessorPrefix(sSig);
        if (sPrefix == null)
            {
            return null;
            }

        // get the property name
        String sProp = sSig.substring(sPrefix.length(), sSig.indexOf('('));
        if (sProp.length() > 0)
            {
            return sProp;
            }

        return null;
        }


    // ----- related behaviors (helpers)

    /**
    * Determine if the specified Property accessor is applicable to the
    * Property as it is declared; in other words, determine if the
    * specified Property accessor should exist.
    *
    * @param nAccessor  the enumerated Property accessor (PA_) value
    *
    * @return true if the accessor should exist, false otherwise
    */
    public boolean isAccessorApplicable(int nAccessor)
        {
        int nIndexed = getIndexed();
        int nDir     = getDirection();

        switch (nAccessor)
            {
            case PA_GET_SINGLE:
                return nIndexed == PROP_SINGLE  && nDir != DIR_IN;

            case PA_SET_SINGLE:
                return nIndexed == PROP_SINGLE  && nDir != DIR_OUT;

            case PA_GET_ARRAY:
                return nIndexed == PROP_INDEXED && nDir != DIR_IN;

            case PA_SET_ARRAY:
                return nIndexed == PROP_INDEXED && nDir != DIR_OUT;

            case PA_GET_INDEX:
                return nIndexed != PROP_SINGLE  && nDir != DIR_IN;

            case PA_SET_INDEX:
                return nIndexed != PROP_SINGLE  && nDir != DIR_OUT;

            default:
                throw new IllegalArgumentException(CLASS + ".isAccessorApplicable:  Illegal accessor value!");
            }
        }


    /**
    * Get the specified accessor Behavior related to this Property.
    *
    * @return the related Behavior, or null
    */
    public Behavior getAccessor(int nAccessor)
        {
        return getComponent().getBehavior(getAccessorSignature(nAccessor));
        }


    /**
    * Get the specified accessor Behavior related to this Property.
    *
    *
    * @return the related Behavior if the accessor applicable and exists;
    *         null otherwise
    */
    public Behavior getApplicableAccessor(int nAccessor)
        {
        return isAccessorApplicable(nAccessor) ? getAccessor(nAccessor) : null;
        }


    /**
    * Get the specified accessor Behavior related to this Property.
    *
    * @return the related Behavior, or null
    */
    public String getAccessorSignature(int nAccessor)
        {
        DataType dt    = m_dt;
        String   sGet  = (!m_fBooleanGet && dt == BOOLEAN) ? "is" : "get";
        String   sName = m_sName;
        String   sType = dt.getTypeString();

        switch (nAccessor)
            {
            case PA_GET_SINGLE:
                return sGet + sName + "()";

            case PA_SET_SINGLE:
                return "set" + sName + '(' + sType + ')';

            case PA_GET_ARRAY:
                return sGet + sName + "()";

            case PA_SET_ARRAY:
                return "set" + sName + "([" + sType + ')';

            case PA_GET_INDEX:
                return sGet + sName + "(I)";

            case PA_SET_INDEX:
                return "set" + sName + "(I" + sType + ')';

            default:
                throw new IllegalArgumentException(CLASS + ".getAccessorSignature:  Illegal accessor value!");
            }
        }

    /**
    * Get all the Behaviors related to this Property.
    *
    * @return an array of six Behaviors, any of which may be null, indexed
    *         by the PA_ (Property Accessors) constants.
    */
    public Behavior[] getAccessors()
        {
        Behavior[] aBehavior = new Behavior[6];
        DataType   dt        = m_dt;
        String     sGet      = (!m_fBooleanGet && dt == BOOLEAN) ? "is" : "get";
        String     sName     = m_sName;
        String     sType     = dt.getTypeString();
        int        nDir      = getDirection();
        int        nIndexed  = getIndexed();
        Component cd = getComponent();

        switch (nIndexed)
            {
            case PROP_SINGLE:
                if ((nDir & DIR_IN) != 0)
                    {
                    String sSig = "set" + sName + '(' + sType + ')';
                    aBehavior[PA_SET_SINGLE] = cd.getBehavior(sSig);
                    }
                if ((nDir & DIR_OUT) != 0)
                    {
                    String sSig = sGet + sName + "()";
                    aBehavior[PA_GET_SINGLE] = cd.getBehavior(sSig);
                    }
                break;

            case PROP_INDEXED:
                if ((nDir & DIR_IN) != 0)
                    {
                    String sSig = "set" + sName + "([" + sType + ')';
                    aBehavior[PA_SET_ARRAY] = cd.getBehavior(sSig);
                    }
                if ((nDir & DIR_OUT) != 0)
                    {
                    String sSig = sGet + sName + "()";
                    aBehavior[PA_GET_ARRAY] = cd.getBehavior(sSig);
                    }
                // no break;

            case PROP_INDEXEDONLY:
                if ((nDir & DIR_IN) != 0)
                    {
                    String sSig = "set" + sName + "(I" + sType + ')';
                    aBehavior[PA_SET_INDEX] = cd.getBehavior(sSig);
                    }
                if ((nDir & DIR_OUT) != 0)
                    {
                    String sSig = sGet + sName + "(I)";
                    aBehavior[PA_GET_INDEX] = cd.getBehavior(sSig);
                    }
                break;
            }

        return aBehavior;
        }

    /**
    * Verify that the specified Property accessor is owned only by the
    * Property (although it may not be removable because it may have a
    * manual origin).
    *
    * @param nAccessor  the enumerated Property accessor (PA_) value
    */
    protected boolean isAccessorOwned(int nAccessor)
        {
        return isAccessorOwned(getAccessor(nAccessor));
        }

    /**
    * Verify that the specified Property accessor is owned only by the
    * Property (although it may not be removable because it may have a
    * manual origin).
    *
    * @param behavior  the accessor
    */
    protected boolean isAccessorOwned(Behavior behavior)
        {
        if (behavior != null &&
               (behavior.isFromImplements()  ||
                behavior.isFromDispatches()  ||
                behavior.isFromIntegration()  ))
            {
            return false;
            }

        return true;
        }

    /**
    * Add all related Property accessors.
    */
    protected void addAccessors()
        {
        // constants do not have accessors
        if (isConstant())
            {
            return;
            }

        boolean fSuper  = isFromSuper();
        boolean fStatic = isStatic();

        for (int i = PA_FIRST; i <= PA_LAST; ++i)
            {
            if (isAccessorApplicable(i))
                {
                Behavior behavior = getAccessor(i);
                if (behavior == null)
                    {
                    // only add accessors if neccesary at the declaration
                    // derivation level
                    if (!fSuper)
                        {
                        behavior = addAccessor(i);
                        }
                    }
                else
                    {
                    // make sure the Behavior matches the Property
                    // (this happens a lot when a Component derivation
                    // resolves which declares a Property)
                    DataType dtRet = m_dt;
                    String   sName = m_sName;
                    switch (i)
                        {
                        case PA_GET_SINGLE:
                            sName = ((!m_fBooleanGet && dtRet == BOOLEAN) ? "is" : "get") + sName;
                            break;

                        case PA_SET_SINGLE:
                            dtRet = DataType.VOID;
                            sName = "set" + sName;
                            break;

                        case PA_GET_ARRAY:
                            dtRet = dtRet.getArrayType();
                            sName = ((!m_fBooleanGet && dtRet == BOOLEAN) ? "is" : "get") + sName;
                            break;

                        case PA_SET_ARRAY:
                            dtRet = DataType.VOID;
                            sName = "set" + sName;
                            break;

                        case PA_GET_INDEX:
                            sName = ((!m_fBooleanGet && dtRet == BOOLEAN) ? "is" : "get") + sName;
                            break;

                        case PA_SET_INDEX:
                            dtRet = DataType.VOID;
                            sName = "set" + sName;
                            break;

                        default:
                            throw new IllegalArgumentException(CLASS + ".addAccessors:  "
                                    + "Illegal accessor value!");
                        }

                    try
                        {
                        // name is case-sensitive
                        behavior.setName(sName, false);

                        // return value is based on the accessor type (above)
                        behavior.getReturnValue().setDataType(dtRet, false);

                        // static Properties have static accessors
                        behavior.setStatic(fStatic, false);
                        }
                    catch (PropertyVetoException e)
                        {
                        // assert
                        throw new IllegalStateException();
                        }

                    // accessor has this property as an origin
                    behavior.addOriginTrait(this);
                    }
                }
            }
        }

    /**
     * Generate a parameter name for a property accessor.
     *
     * @param type       the property type
     * @param sPropName  the property name
     *
     * @return the parameter name
     */
    public static String makeParamName(DataType type, String sPropName)
        {
        String sName = "";

        while (type.isArray())
            {
            sName += "a";
            type  = type.getElementType();
            }

        if (type.isPrimitive())
            {
            switch (type.getType().getSignature().charAt(0))
                {
                case 'Z':
                    sName += "f";
                    break;

                case 'C':
                    sName += "ch";
                    break;

                case 'B':
                    sName += "b";
                    break;

                case 'S':
                case 'I':
                   sName += "n";
                   break;

                case 'J':
                    sName += "l";
                    break;

                case 'F':
                    sName += "fl";
                    break;

                case 'D':
                    sName += "d";
                    break;
                }
            }
        else // class or component
            {
            String sTypeName = type.isClass() ? type.getClassName() : type.getComponentName();

            sTypeName = sTypeName.substring(Math.max(sTypeName.lastIndexOf('.'), sTypeName.lastIndexOf('$')) + 1); // drop the package name

            if (sTypeName.equals("String"))
                {
                sName += "s";
                }
            else if (sTypeName.endsWith("Exception") || sTypeName.endsWith("Error") || sTypeName.equals("Throwable"))
                {
                sName += "e";
                }
            else if (sTypeName.equals("Class"))
                {
                sName += "clz";
                }
            else if (sTypeName.equals("Collection"))
                {
                sName += "col";
                }
            else if (sTypeName.equals("Object"))
                {
                sName += "o";
                }
            else if (sTypeName.endsWith("Iterator"))
                {
                sName += "iter";
                }
            else if (sTypeName.endsWith("Event"))
                {
                sName += "evt";
                }
            else if (sTypeName.equals("Binary"))
                {
                sName += "bin";
                }
            else if (sTypeName.endsWith("Message"))
                {
                sName += "msg";
                }
            else if (sTypeName.endsWith("Buffer"))
                {
                sName += "buf";
                }
            else if (sTypeName.endsWith("Map"))
                {
                sName += "map";
                }
            else if (sTypeName.startsWith("Atomic"))
                {
                sName += "atomic";
                }
            else
                {
                // find last part of a camel case type name
                char[] ach = sTypeName.toCharArray();
                int ofStart;
                for (ofStart = ach.length - 1; ofStart > 0 && Character.isLowerCase(ach[ofStart]); --ofStart)
                    {}

                sName += sTypeName.substring(ofStart).toLowerCase();
                }
            }

        // find last part of a camel case property name
        char[] ach = sPropName.toCharArray();
        int ofStart;
        for (ofStart = ach.length - 1; ofStart > 0 && Character.isLowerCase(ach[ofStart]); --ofStart)
            {}
        String sProp = sPropName.substring(ofStart, ofStart + 1).toUpperCase() + sPropName.substring(ofStart + 1);

        if (sName.endsWith(sProp.toLowerCase()))
            {
            // the end of the property name matches the end of the type name, i.e. java.util.Set for MemberSet
            if (ofStart > 0)
                {
                // select next "word" from the property name
                int ofEnd = ofStart;
                --ofStart;
                for (; ofStart > 0 && Character.isLowerCase(ach[ofStart]); --ofStart)
                    {}

                sName += sPropName.substring(ofStart, ofStart + 1).toUpperCase() + sPropName.substring(ofStart + 1, ofEnd);
                }
            }
        else
            {
            sName += sProp;
            }

        return sName;
        }

    /**
    * Add the specified Property accessor.
    *
    * @param nAccessor  the enumerated Property accessor (PA_) value
    */
    protected Behavior addAccessor(int nAccessor)
        {
        DataType   dtRet    = DataType.VOID;
        String     sName    = null;
        DataType[] adtParam = null;
        String[]   asParam  = null;

        switch (nAccessor)
            {
            case PA_GET_SINGLE:
                dtRet = m_dt;
                sName = ((!m_fBooleanGet && m_dt == BOOLEAN) ? "is" : "get") + m_sName;
                break;

            case PA_SET_SINGLE:
                sName    = "set" + m_sName;
                adtParam = new DataType[] {m_dt};
                asParam  = new String[] {makeParamName(m_dt, m_sName)};
                break;

            case PA_GET_ARRAY:
                dtRet = m_dt.getArrayType();
                sName = ((!m_fBooleanGet && m_dt == BOOLEAN) ? "is" : "get") + m_sName;
                break;

            case PA_SET_ARRAY:
                sName    = "set" + m_sName;
                adtParam = new DataType[] {m_dt.getArrayType()};
                asParam  = new String[] {makeParamName(adtParam[0], m_sName)};
                break;

            case PA_GET_INDEX:
                dtRet    = m_dt;
                sName    = ((!m_fBooleanGet && m_dt == BOOLEAN) ? "is" : "get") + m_sName;
                adtParam = new DataType[] {INT};
                asParam  = new String[] {INDEX_PARAM};
                break;

            case PA_SET_INDEX:
                sName    = "set" + m_sName;
                adtParam = new DataType[] {INT, m_dt};
                asParam  = new String[] {INDEX_PARAM, makeParamName(m_dt, m_sName)};
                break;

            default:
                throw new IllegalArgumentException(CLASS + ".addAccessor:  "
                        + "Illegal accessor value!");
            }

        Component cd       = getComponent();
        Behavior  behavior = new Behavior(cd, dtRet, sName, isStatic(),
                getDefaultAccess(nAccessor), adtParam, asParam, null);
        behavior.addOriginTrait(this);

        // assertion:  remove after testing
        // verify that the Behavior does not exist already
        if (cd.getBehavior(behavior.getSignature()) != null)
            {
            throw new IllegalStateException(CLASS + ".addAccessor:  "
                    + "Behavior already exists!");
            }

        try
            {
            cd.addBehavior(behavior, false);
            }
        catch (PropertyVetoException e)
            {
            // assert
            throw new IllegalStateException();
            }

        return behavior;
        }

    /**
    * Determine the default access for a new Property accessor Behavior that
    * has the specified type, name, and direction.
    *
    * @param nAccessor  the proposed Property accessor (PA_)
    *
    * @return the best-guess default accessibility value for the accessor
    */
    protected int getDefaultAccess(int nAccessor)
        {
        Behavior[] aBehavior = getAccessors();

        int nDefault1 = -1;
        int nDefault2 = -1;

        switch (nAccessor)
            {
            case PA_GET_SINGLE:
                nDefault1 = PA_SET_SINGLE;
                break;

            case PA_SET_SINGLE:
                nDefault1 = PA_GET_SINGLE;
                break;

            case PA_GET_ARRAY:
                nDefault1 = PA_GET_INDEX;
                nDefault2 = PA_SET_ARRAY;
                break;

            case PA_SET_ARRAY:
                nDefault1 = PA_SET_INDEX;
                nDefault2 = PA_GET_ARRAY;
                break;

            case PA_GET_INDEX:
                nDefault1 = PA_GET_ARRAY;
                nDefault2 = PA_SET_INDEX;
                break;

            case PA_SET_INDEX:
                nDefault1 = PA_SET_ARRAY;
                nDefault2 = PA_GET_INDEX;
                break;

            default:
                throw new IllegalArgumentException(CLASS + ".getDefaultAccess:  Illegal accessor value!");
            }

        Behavior behavior = aBehavior[nDefault1];
        if (behavior == null && nDefault2 >= 0)
            {
            behavior = aBehavior[nDefault2];
            }

        return (behavior == null ? ACCESS_PRIVATE : behavior.getAccess());
        }

    /**
    * Verify that the specified Property accessor is removable.
    *
    * @param nAccessor  the enumerated Property accessor (PA_) value
    */
    protected boolean isAccessorRemovable(int nAccessor)
        {
        Behavior behavior = getAccessor(nAccessor);

        // note:  assumption that behavior cannot be from super or base
        // because this method is only called at the declaration level
        // of the property

        if (behavior != null &&
            (
             behavior.isFromImplements()  ||
             behavior.isFromDispatches()  ||
             behavior.isFromIntegration() ||
             behavior.isFromManual()       ))
            {
            return false;
            }

        return true;
        }

    /**
    * Remove the specified Property accessor.
    *
    * @param nAccessor  the enumerated Property accessor (PA_) value
    */
    protected void removeAccessor(int nAccessor)
        {
        Component cd       = getComponent();
        String    sSig     = getAccessorSignature(nAccessor);
        Behavior  behavior = cd.getBehavior(sSig);

        if (behavior != null)
            {
            // assertion:  remove after testing
            // the related Behaviors can only have Property origin
            if (!behavior.isFromProperty()       ||
                    behavior.isFromSuper()       ||
                    behavior.isFromImplements()  ||
                    behavior.isFromDispatches()  ||
                    behavior.isFromIntegration() ||
                    behavior.isFromManual()       )
                {
                throw new IllegalStateException(CLASS + ".removeAccessor:  " +
                    "Illegal Behavior origin!  (" + behavior.toString() + ")");
                }

            try
                {
                cd.removeBehavior(behavior, false);
                }
            catch (PropertyVetoException e)
                {
                // assert
                throw new IllegalStateException();
                }
            }
        }


    // ----- Origin

    /**
    * Determine if this Property exists as a result of integration at this
    * level.
    *
    * @return true if this Property results from integration at this level
    */
    public boolean isFromIntegration()
        {
        return isFromTraitDescriptor(Integration.DESCRIPTOR);
        }

    /**
    * Resolve any conflicts such that this Property matches the passed
    * field declaration for integration.
    *
    * @param map    declaring integration
    * @param field  the Java field (or null to remove integration)
    */
    protected void setFromIntegration(Integration map, Property field)
        {
        // update access
        if (map == null)
            {
            throw new IllegalArgumentException(CLASS + ".setFromIntegrates:  "
                    + "Integrates interface required!");
            }

        if (field == null)
            {
            // integration is being removed
            removeOriginTrait(map);
            }
        else
            {
            // integration is being added
            try
                {
                // determine value for the property; constants get
                // their value from the field; properties can have
                // a design-time value, but the value is invalidated
                // if the datatypes or indexed attributes don't match
                Object oPrev  = field.m_oValue;
                Object oValue = this .m_oValue;
                if (field.isConstant()
                    || this.m_dt         != field.m_dt
                    || this.getIndexed() != field.getIndexed())
                    {
                    oValue = oPrev;
                    }

                // store the previous (aka the field's) value
                m_oPrevValue = oPrev;

                // synchronize this property to the integrated field
                setStatic    (field.isStatic()    , false);
                setFinal     (field.isFinal()     , false);
                setPersistent(field.isPersistent(), false);
                setDirection (field.getDirection(), false);
                setDataType  (field.getDataType() , false);
                setIndexed   (field.getIndexed()  , false);
                setValue     (oValue              , false);

                // update origin
                addOriginTrait(map);
                }
            catch (PropertyVetoException e)
                {
                // assert
                throw new IllegalStateException();
                }
            }
        }


    // ----- Exists

    /**
    * Get the exists flag for this Property.
    *
    * For a JCS, a Property is EXISTS_DELETE if multiple "base" (super and/or
    * interface) classes declare the field and the definitions contradicit.
    * The JLS refers to this as "the field is ambiguous".
    *
    * For a JCS, a Property is EXISTS_NOT if a super or interface declares
    * the field but the field is not present in the class represented by
    * the JCS.
    *
    * @return either EXISTS_INSERT or EXISTS_UPDATE
    *         (or EXISTS_NOT or EXISTS_DELETE for a JCS)
    */
    public int getExists()
        {
        return m_nFlags & EXISTS_MASK;
        }

    /**
    * Set the exists flag for this Property.
    *
    * @param nExists  the exists flag for this Property
    */
    protected void setExists(int nExists)
        {
        m_nFlags = m_nFlags & ~EXISTS_MASK | nExists;
        }


    // ----- Access

    /**
    * Determine the current accessibility of the Property.
    *
    * @return either ACCESS_PUBLIC, ACCESS_PROTECTED, or ACCESS_PRIVATE
    *
    * @see com.tangosol.dev.component.Constants#ACCESS_PUBLIC
    * @see com.tangosol.dev.component.Constants#ACCESS_PROTECTED
    * @see com.tangosol.dev.component.Constants#ACCESS_PRIVATE
    */
    public int getAccess()
        {
        return m_nFlags & ACCESS_MASK;
        }

    /**
    * Determine if the accessibility of the Property can be modified.  Like
    * most attributes, the access can only be set at the Property declaration
    * level.  Only constant Properties (either Java or virtual) can have
    * their access modified.  The access attribute can only be set for
    * constant Properties.
    *
    * @return true if the access attribute of the Property can be set
    */
    public boolean isAccessSettable()
        {
        return isDeclaredAtThisLevel()
            && isModifiable()
            && isConstant();
        }

    /**
    * Determine if the specified value is acceptable for the access
    * attribute.  Only constant Properties can have their access changed;
    * non-constant Properties are always private (meaning that any Java
    * field created to store the value of the Property will be private.)
    *
    * Java constants can be public (for example, constants used in an API),
    * protected (constants used in this Component or in derived Components),
    * or private (constants only used in this Component).
    *
    * Virtual constants can be either public or protected.  Virtual constants
    * cannot be private (because private means non-virtual).
    *
    * @return true if the specified value is acceptable for the access
    *         attribute
    *
    * @see com.tangosol.dev.component.Constants#ACCESS_PUBLIC
    * @see com.tangosol.dev.component.Constants#ACCESS_PROTECTED
    * @see com.tangosol.dev.component.Constants#ACCESS_PRIVATE
    */
    public boolean isAccessLegal(int nAccess)
        {
        switch (nAccess)
            {
            case ACCESS_PUBLIC:
            case ACCESS_PROTECTED:
                return isConstant();

            case ACCESS_PRIVATE:
                return !isVirtualConstant();

            default:
                return false;
            }
        }

    /**
    * Set the accessibility of the Property.
    *
    * @param nAccess  ACCESS_PUBLIC, ACCESS_PROTECTED, or ACCESS_PRIVATE
    *
    * @exception PropertyVetoException if the property cannot be set as
    *            specified or was vetoed by a property listener
    *
    * @see com.tangosol.dev.component.Constants#ACCESS_PUBLIC
    * @see com.tangosol.dev.component.Constants#ACCESS_PROTECTED
    * @see com.tangosol.dev.component.Constants#ACCESS_PRIVATE
    */
    public void setAccess(int nAccess)
            throws PropertyVetoException
        {
        setAccess(nAccess, true);
        }

    /**
    * Set the accessibility of the Property.
    *
    * @param nAccess    ACCESS_PUBLIC, ACCESS_PROTECTED, or ACCESS_PRIVATE
    * @param fVetoable  true if the setter can veto the value
    *
    * @exception PropertyVetoException if the property cannot be set as
    *            specified or was vetoed by a property listener
    */
    protected synchronized void setAccess(int nAccess, boolean fVetoable)
            throws PropertyVetoException
        {
        int nPrev = getAccess();

        if (nAccess == nPrev)
            {
            return;
            }

        Integer prev  = nPrev;
        Integer value = nAccess;

        if (fVetoable)
            {
            if (!isAccessSettable())
                {
                readOnlyAttribute(ATTR_ACCESS, prev, value);
                }

            if (!isAccessLegal(nAccess))
                {
                illegalAttributeValue(ATTR_ACCESS, prev, value);
                }

            fireVetoableChange(ATTR_ACCESS, prev, value);
            }

        m_nFlags = m_nFlags & ~ACCESS_MASK | nAccess;
        firePropertyChange(ATTR_ACCESS, prev, value);
        }


    // ----- Static

    /**
    * Determine the current scope of the Property.
    *
    * @return true if the Property is static (shared among instances)
    */
    public boolean isStatic()
        {
        return (m_nFlags & SCOPE_MASK) == SCOPE_STATIC;
        }

    /**
    * Determine if the scope of the Property can be modified.  The Property
    * must be declared at this level.
    *
    * - Java constants can modify static freely (assuming they do not
    *   originate from integration).
    * - Virtual constants cannot be static, so static is not settable.
    * - All other Properties can only modify static if it doesn't cause a
    *   conflict with Behaviors that have multiple origins (for example,
    *   Behaviors from an interface).
    *
    * @return true if the scope attribute of the Property can be set
    */
    public boolean isStaticSettable()
        {
        if (isDeclaredAtThisLevel() && isModifiable() && !isFromIntegration())
            {
            if (isConstant())
                {
                return isJavaConstant(); // !isVirtualConstant()
                }
            else // functional/calculated/standard property
                {
                for (int i = PA_FIRST; i <= PA_LAST; ++i)
                    {
                    if (!isAccessorOwned(i))
                        {
                        return false;
                        }
                    }
                return true;
                }
            }

        return false;
        }

    /**
    * Set the scope attribute of the Property.
    *
    * @param fStatic the new scope value
    *
    * @exception PropertyVetoException if the property cannot be set as
    *            specified or was vetoed by a property listener
    */
    public void setStatic(boolean fStatic)
            throws PropertyVetoException
        {
        setStatic(fStatic, true);
        }

    /**
    * Set the scope attribute of the Property.
    *
    * @param fStatic    the new scope value
    * @param fVetoable  true if the setter can veto the value
    *
    * @exception PropertyVetoException if the property cannot be set as
    *            specified or was vetoed by a property listener
    */
    protected synchronized void setStatic(boolean fStatic, boolean fVetoable)
            throws PropertyVetoException
        {
        boolean fPrevStatic = isStatic();

        if (fStatic == fPrevStatic)
            {
            return;
            }

        Boolean prev  = toBoolean(fPrevStatic);
        Boolean value = toBoolean(fStatic);

        if (fVetoable)
            {
            if (!isStaticSettable())
                {
                readOnlyAttribute(ATTR_STATIC, prev, value);
                }

            fireVetoableChange(ATTR_STATIC, prev, value);
            }

        // change the static setting
        m_nFlags = m_nFlags & ~SCOPE_MASK | (fStatic ? SCOPE_STATIC : SCOPE_INSTANCE);

        if (!isConstant())
            {
            // set any related behaviors to static
            Behavior[] aBehavior = getAccessors();
            int        cBehavior = aBehavior.length;

            for (int i = 0; i < cBehavior; ++i)
                {
                Behavior behavior = aBehavior[i];
                if (behavior == null)
                    {
                    continue;
                    }

                // assertion: remove after testing
                // Behavior must have Property as an origin
                if (!behavior.isFromProperty())
                    {
                    throw new IllegalStateException(CLASS + ".setStatic:  "
                            + "Behavior does not have Property as origin! ("
                            + behavior.toString() + ")");
                    }

                try
                    {
                    behavior.setStatic(fStatic, false);
                    }
                catch (PropertyVetoException e)
                    {
                    // assertion:  the setter was not vetoable
                    throw new IllegalStateException(CLASS + ".setStatic:  "
                            + "Behavior setStatic vetoed!");
                    }
                }
            }

        firePropertyChange(ATTR_STATIC, prev, value);
        }


    // ----- Final

    /**
    * Determine the current finality of the Property.
    *
    * @return true if the Property is final (i.e. as in a final Java field)
    */
    public boolean isFinal()
        {
        return (m_nFlags & DERIVE_MASK) == DERIVE_FINAL;
        }

    /**
    * Determine if the finality of the Property can be modified.  The
    * Property must be declared at this level.  Final only applies to
    * constant Properties; a final constant Property is a Java constant
    * and a derivable constant Property is a virtual constant.
    *
    * - Virtual constants can be made final (i.e. into Java constants)
    * - Java constants can only be made derivable (i.e. into virtual
    *   constants) if all of the following are true:
    *   - the Property does not originate from integration
    *   - the Property is not static
    *   - the Property is not private
    *
    * @return true if the final attribute of the Property can be set
    */
    public boolean isFinalSettable()
        {
        return isDeclaredAtThisLevel()
            && isModifiable()
            && isConstant()
            && !isFromIntegration()
            && !isStatic()
            && getAccess() != ACCESS_PRIVATE;
        }

    /**
    * Set the finality attribute of the Property.
    *
    * @param fFinal the new finality value
    *
    * @exception PropertyVetoException if the property cannot be set as
    *            specified or was vetoed by a property listener
    */
    public void setFinal(boolean fFinal)
            throws PropertyVetoException
        {
        setFinal(fFinal, true);
        }

    /**
    * Set the finality attribute of the Property.
    *
    * @param fFinal     the new finality value
    * @param fVetoable  true if the setter can veto the value
    *
    * @exception PropertyVetoException if the property cannot be set as
    *            specified or was vetoed by a property listener
    */
    protected synchronized void setFinal(boolean fFinal, boolean fVetoable)
            throws PropertyVetoException
        {
        boolean fPrevFinal = isFinal();

        if (fFinal == fPrevFinal)
            {
            return;
            }

        Boolean prev  = toBoolean(fPrevFinal);
        Boolean value = toBoolean(fFinal);

        if (fVetoable)
            {
            if (!isFinalSettable())
                {
                readOnlyAttribute(ATTR_FINAL, prev, value);
                }

            fireVetoableChange(ATTR_FINAL, prev, value);
            }

        m_nFlags = m_nFlags & ~DERIVE_MASK | (fFinal ? DERIVE_FINAL : DERIVE_DERIVABLE);

        // Java constants cannot be indexed-only
        if (fFinal && getIndexed() == PROP_INDEXEDONLY)
            {
            setIndexed(PROP_INDEXED, false);
            }

        firePropertyChange(ATTR_FINAL, prev, value);
        }


    // ----- Visible

    /**
    * Get the visibility attribute of the Property.
    *
    * @return the value of the Property's visbility attribute
    *
    * @see com.tangosol.dev.component.Constants#VIS_SYSTEM
    * @see com.tangosol.dev.component.Constants#VIS_HIDDEN
    * @see com.tangosol.dev.component.Constants#VIS_ADVANCED
    * @see com.tangosol.dev.component.Constants#VIS_VISIBLE
    */
    public int getVisible()
        {
        return m_nFlags & VIS_MASK;
        }

    /**
    * Determine whether the Visible attribute of the Property can be set.
    *
    * @return true if the Visible attribute can set with a valid value
    */
    public boolean isVisibleSettable()
        {
        return !isComponentComplex() && isModifiable();
        }

    /**
    * Determine whether the Visible attribute of the Property can be set to
    * the specified value.  The Visible attribute is flexible.
    *
    * @param nVis the new value of the visibility attribute
    *
    * @return true if the specified value is acceptable
    *
    * @see com.tangosol.dev.component.Constants#VIS_SYSTEM
    * @see com.tangosol.dev.component.Constants#VIS_HIDDEN
    * @see com.tangosol.dev.component.Constants#VIS_ADVANCED
    * @see com.tangosol.dev.component.Constants#VIS_VISIBLE
    */
    public boolean isVisibleLegal(int nVis)
        {
        switch (nVis)
            {
            case VIS_SYSTEM:
            case VIS_HIDDEN:
            case VIS_ADVANCED:
            case VIS_VISIBLE:
                return true;
            default:
                return false;
            }
        }

    /**
    * Set the visible attribute of the Property.
    *
    * @param nVis the new visibility value
    *
    * @exception PropertyVetoException if the property cannot be set as
    *            specified or was vetoed by a property listener
    *
    * @see com.tangosol.dev.component.Constants#VIS_SYSTEM
    * @see com.tangosol.dev.component.Constants#VIS_HIDDEN
    * @see com.tangosol.dev.component.Constants#VIS_ADVANCED
    * @see com.tangosol.dev.component.Constants#VIS_VISIBLE
    */
    public void setVisible(int nVis)
            throws PropertyVetoException
        {
        setVisible(nVis, true);
        }

    /**
    * Set the visible attribute of the Property.
    *
    * @param nVis       the new visibility value
    * @param fVetoable  true if the setter can veto the value
    *
    * @exception PropertyVetoException if the property cannot be set as
    *            specified or was vetoed by a property listener
    */
    protected synchronized void setVisible(int nVis, boolean fVetoable)
            throws PropertyVetoException
        {
        int nPrevVis = getVisible();

        if (nVis == nPrevVis)
            {
            return;
            }

        Integer prev  = nPrevVis;
        Integer value = nVis;

        if (fVetoable)
            {
            if (!isVisibleSettable())
                {
                readOnlyAttribute(ATTR_VISIBLE, prev, value);
                }

            if (!isVisibleLegal(nVis))
                {
                illegalAttributeValue(ATTR_VISIBLE, prev, value);
                }

            fireVetoableChange(ATTR_VISIBLE, prev, value);
            }

        m_nFlags = m_nFlags & ~VIS_MASK | nVis;
        firePropertyChange(ATTR_VISIBLE, prev, value);
        }


    // ----- Deprecated

    /**
    * Get the antiquity attribute of the Property.
    *
    * @return true if the Property is deprecated
    */
    public boolean isDeprecated()
        {
        return (m_nFlags & ANTIQ_MASK) == ANTIQ_DEPRECATED;
        }

    /**
    * Determine whether the antiquity attribute of the Property can be set.
    * Since the antiquity attribute is expressed as a boolean,  this method
    * doubles for both the "is settable" and "is legal" questions. The
    * antiquity attribute of the Property is flexible.
    *
    * @return true if the antiquity attribute can set
    */
    public boolean isDeprecatedSettable()
        {
        return !isComponentComplex() && isModifiable();
        }

    /**
    * Set the antiquity attribute of the Property.
    *
    * @param fDeprecated the new antiquity attribute value
    *
    * @exception PropertyVetoException if the property cannot be set as
    *            specified or was vetoed by a property listener
    */
    public void setDeprecated(boolean fDeprecated)
            throws PropertyVetoException
        {
        setDeprecated(fDeprecated, true);
        }

    /**
    * Set the antiquity attribute of the Property.
    *
    * @param fDeprecated  the new antiquity attribute value
    * @param fVetoable    true if the setter can veto the value
    *
    * @exception PropertyVetoException if the property cannot be set as
    *            specified or was vetoed by a property listener
    */
    protected synchronized void setDeprecated(boolean fDeprecated, boolean fVetoable)
            throws PropertyVetoException
        {
        boolean fPrevDeprecated = isDeprecated();

        if (fDeprecated == fPrevDeprecated)
            {
            return;
            }

        Boolean prev  = toBoolean(fPrevDeprecated);
        Boolean value = toBoolean(fDeprecated);

        if (fVetoable)
            {
            if (!isDeprecatedSettable())
                {
                readOnlyAttribute(ATTR_DEPRECATED, prev, value);
                }

            fireVetoableChange(ATTR_DEPRECATED, prev, value);
            }

        m_nFlags = m_nFlags & ~ANTIQ_MASK | (fDeprecated ? ANTIQ_DEPRECATED : ANTIQ_CURRENT);
        firePropertyChange(ATTR_DEPRECATED, prev, value);
        }


    // ----- Persistent

    /**
    * Get the persistence attribute of the Property.  Persistence has
    * slightly different meanings based on whether the Property is
    * gettable and/or settable:
    *
    * For a gettable-only Property, persistent means constant and transient
    * means calculated.
    *
    * For gettable/settable Properties, persistent means that any Java field
    * that results from the Property will be subject to serialization by
    * Java's serialization mechanism.
    *
    * For settable-only Properties, persistent has no meaning; settable-only
    * Properties are always marked transient.
    *
    * @return true if the Property is persistent
    */
    public boolean isPersistent()
        {
        return (m_nFlags & STG_MASK) == STG_PERSIST;
        }

    /**
    * Determine whether the persistence attribute of the Property can be
    * set.  Since the persistence attribute is expressed as a boolean,  this
    * method doubles for both the "is settable" and "is legal" questions. The
    * persistence attribute of the Property is only settable for gettable/
    * settable Properties.  (This means that constant Properties cannot be
    * made non-constant.)
    *
    * @return true if the persistence attribute can set
    */
    public boolean isPersistentSettable()
        {
        return isDeclaredAtThisLevel()
            && isStandardProperty()
            && isModifiable()
            && !isFromIntegration();
        }

    /**
    * Set the persistence attribute of the Property.
    *
    * @param fPersistent the new persistence attribute value
    *
    * @exception PropertyVetoException if the property cannot be set as
    *            specified or was vetoed by a property listener
    */
    public void setPersistent(boolean fPersistent)
            throws PropertyVetoException
        {
        setPersistent(fPersistent, true);
        }

    /**
    * Set the persistence attribute of the Property.
    *
    * @param fPersistent  the new persistence attribute value
    * @param fVetoable    true if the setter can veto the value
    *
    * @exception PropertyVetoException if the property cannot be set as
    *            specified or was vetoed by a property listener
    */
    protected synchronized void setPersistent(boolean fPersistent, boolean fVetoable)
            throws PropertyVetoException
        {
        boolean fPrevPersistent = isPersistent();

        if (fPersistent == fPrevPersistent)
            {
            return;
            }

        Boolean prev  = toBoolean(fPrevPersistent);
        Boolean value = toBoolean(fPersistent);

        if (fVetoable)
            {
            if (!isPersistentSettable())
                {
                readOnlyAttribute(ATTR_PERSISTENT, prev, value);
                }

            fireVetoableChange(ATTR_PERSISTENT, prev, value);
            }

        m_nFlags = m_nFlags & ~STG_MASK | (fPersistent ? STG_PERSIST : STG_TRANSIENT);
        firePropertyChange(ATTR_PERSISTENT, prev, value);
        }


    // ----- Direction

    /**
    * Access the Property direction attribute.
    *
    * DIR_IN    - settable only:  functional Properties
    * DIR_OUT   - gettable only:  constants, calculated Properties
    * DIR_INOUT - gettable/settable:  standard Properties
    *
    * @return the direction attribute of the Property
    *
    * @see com.tangosol.dev.component.Constants#DIR_IN
    * @see com.tangosol.dev.component.Constants#DIR_OUT
    * @see com.tangosol.dev.component.Constants#DIR_INOUT
    */
    public int getDirection()
        {
        return m_nFlags & DIR_MASK;
        }

    /**
    * Determine if the Property is settable (direction attribute is in or
    * in/out).
    *
    * @return true if the Property is settable
    */
    public boolean isSettable()
        {
        return (m_nFlags & DIR_IN) != 0;
        }

    /**
    * Determine if the Property is gettable (direction attribute is out or
    * in/out).
    *
    * @return true if the Property is gettable
    */
    public boolean isGettable()
        {
        return (m_nFlags & DIR_OUT) != 0;
        }

    /**
    * Determine if the direction attribute of the Property can be set.  The
    * direction can only be changed if:
    *   1.  the Property is declared at this level
    *   2.  the Property is not a constant
    *   3.  the Property is modifiable
    *
    * @return true if the direction attribute of the Property can be set
    */
    public boolean isDirectionSettable()
        {
        return isDeclaredAtThisLevel()
            && !isConstant()
            && isModifiable()
            && !isFromIntegration();
        }

    /**
    * Determine if the specified direction is a legal direction value for
    * this Property.  The direction can be changed as long as the change
    * will not remove the Property as an origin of a Behavior that has any
    * other origin besides the Property.  (This makes sure that all of the
    * Behaviors that are reserved for this Property are only declared if
    * they have the Property as an origin.)
    *
    * @return true if the specified direction is legal for this Property
    *
    * @see com.tangosol.dev.component.Constants#DIR_IN
    * @see com.tangosol.dev.component.Constants#DIR_OUT
    * @see com.tangosol.dev.component.Constants#DIR_INOUT
    */
    public boolean isDirectionLegal(int nDir)
        {
        boolean fRemoveOut;
        switch (nDir)
            {
            default:
                return false;

            case DIR_INOUT:
                // widening allowed (from in or out to inout)
                return true;

            case DIR_IN:
                fRemoveOut = true;
                break;

            case DIR_OUT:
                fRemoveOut = false;
                break;
            }

        // short-cut:  if direction isn't changing, then the change is legal
        int nPrevDir = getDirection();
        if (nDir == nPrevDir)
            {
            // no change
            return true;
            }

        // get a list of all related Behaviors
        Behavior[] aBehavior = getAccessors();
        int        cBehavior = aBehavior.length;

        // keep the Behaviors in the list that will be removed if the
        // direction changes
        if (fRemoveOut)
            {
            // changing from gettable to not
            return isAccessorRemovable(PA_GET_SINGLE) &&
                   isAccessorRemovable(PA_GET_ARRAY ) &&
                   isAccessorRemovable(PA_GET_INDEX );
            }
        else
            {
            // changing from settable to not
            return isAccessorRemovable(PA_SET_SINGLE) &&
                   isAccessorRemovable(PA_SET_ARRAY ) &&
                   isAccessorRemovable(PA_SET_INDEX );
            }
        }

    /**
    * Set the property direction.
    *
    * @param nDir  the new direction for the property
    *
    * @see com.tangosol.dev.component.Constants#DIR_IN
    * @see com.tangosol.dev.component.Constants#DIR_OUT
    * @see com.tangosol.dev.component.Constants#DIR_INOUT
    */
    public void setDirection(int nDir)
            throws PropertyVetoException
        {
        setDirection(nDir, true);
        }

    /**
    * Set the parameter direction.
    *
    * @param nDir       the new direction for the parameter
    * @param fVetoable  true if the setter can veto the value
    */
    protected synchronized void setDirection(int nDir, boolean fVetoable)
            throws PropertyVetoException
        {
        int nPrev = getDirection();

        if (nDir == nPrev)
            {
            return;
            }

        Integer prev  = nPrev;
        Integer value = nDir;

        if (fVetoable)
            {
            if (!isDirectionSettable())
                {
                readOnlyAttribute(ATTR_DIRECTION, prev, value);
                }

            if (!isDirectionLegal(nDir))
                {
                illegalAttributeValue(ATTR_DIRECTION, prev, value);
                }

            fireVetoableChange(ATTR_DIRECTION, prev, value);
            }

        // if the direction was changed from "inout", then persistent
        // must be set to false (neither "in" nor "out" non-constant
        // Properties can be persistent)
        if (nPrev == DIR_INOUT && isPersistent())
            {
            setPersistent(false, false);
            }

        // if the direction was changed from "inout" (to either "in" or
        // "out"), or if the direction was changed from "in" to "out", or
        // if the direction was changed from "out" to "in", then there
        // are Behaviors (Property accessors) that must be removed from
        // the Component Definition
        if (nDir != DIR_INOUT)
            {
            boolean fRemoveOut = (nDir == DIR_IN);
            switch (getIndexed())
                {
                case PROP_SINGLE:
                    removeAccessor(fRemoveOut ? PA_GET_SINGLE : PA_SET_SINGLE);
                    break;
                case PROP_INDEXED:
                    removeAccessor(fRemoveOut ? PA_GET_ARRAY  : PA_SET_ARRAY );
                    // no break;
                case PROP_INDEXEDONLY:
                    removeAccessor(fRemoveOut ? PA_GET_INDEX  : PA_SET_INDEX );
                    break;
                }
            }

        // change the direction
        m_nFlags = m_nFlags & ~DIR_MASK | nDir;

        // non-constant out-only Properties cannot have a design time value
        if (nDir == DIR_OUT && m_oValue != NO_VALUE)
            {
            setValue(NO_VALUE, false);
            }

        // if the direction was changed from "in" or from "out", then
        // there are additional Behaviors (Property accessors) that must
        // be added to the Component Definition (unless it is now a constant
        // in which case there are no accessors)
        if (nPrev != DIR_INOUT && !isConstant())
            {
            boolean fAddOut = (nPrev == DIR_IN);
            switch (getIndexed())
                {
                case PROP_SINGLE:
                    addAccessor(fAddOut ? PA_GET_SINGLE : PA_SET_SINGLE);
                    break;
                case PROP_INDEXED:
                    addAccessor(fAddOut ? PA_GET_ARRAY  : PA_SET_ARRAY );
                    // no break;
                case PROP_INDEXEDONLY:
                    addAccessor(fAddOut ? PA_GET_INDEX  : PA_SET_INDEX );
                    break;
                }
            }

        firePropertyChange(ATTR_DIRECTION, prev, value);
        }


    // ----- DataType

    /**
    * Access the Property data type.
    *
    * @return the data type of the property
    */
    public DataType getDataType()
        {
        return m_dt;
        }

    /**
    * Determine if the data type is complex, which means that the Property
    * value is a Component Definition.
    *
    * @return true if the data type is complex
    */
    public boolean isComplex()
        {
        return m_dt.isComponent();
        }

    /**
    * Determine if the complex data type is designable, which means that the
    * Property value is a Component Definition and that the Component
    * Definition can have its state designed.
    *
    * @return true if the data type is a designable complex value
    */
    public boolean isComplexDesignable()
        {
        // verify that the Property is complex
        DataType dt = m_dt;
        if (!dt.isComponent())
            {
            return false;
            }

        // get the complex Property constraint
        // (i.e. fully qualified Component name)
        String sConstraint = dt.getComponentName();

        // get the Component which contains this Property
        Component cd = getComponent();

        while (true)
            {
            // if this complex Property derives from the containing
            // Component, then this Property is not designable
            if (Component.isDerivedFrom(sConstraint, cd.getQualifiedName()))
                {
                return false;
                }

            // it is possible that the containing Component is itself
            // a complex Property value
            if (!cd.isComplex())
                {
                return true;
                }

            // get the Property that contains the complex value (cd)
            Property prop = cd.getComplex();

            // check if this complex value is contain by a property
            // if it is not contained by a property, then it is a
            // free floating complex value
            if (prop == null)
                {
                return true;
                }

            // get the Component that contains that Property
            // (repeat the derivation check until we find a cd which
            // is not a complex value or we are not contained by
            // a property)
            cd = prop.getComponent();
            }
        }

    /**
    * Determine if the Property data type can be set.  The data type can
    * be modified if all of the following are true:
    *
    * 1.  The Property is modifiable
    * 2.  The Property is declared at this level
    * 3.  Any related out Behaviors do not originate from an interface,
    *     an event interface, or from integration
    *
    * The last requirement is necessary (and exact) because:
    * 1.  The only origins for the related Behaviors are Property (this
    *     origin must exist), manual, implements interface, dispatches
    *     interface, and integrates.  (The Behavior origin cannot be super
    *     because the Property would not have been creatable if a related
    *     Behavior already existed.)
    *
    * 2.  If the Behavior origin is implements, dispatches, or integrates,
    *     then the declaration (e.g. return value, parameter types, static,
    *     etc.) is fixed.
    *
    * 3.  It is not possible for the Data Type of the Property to change if
    *     an out accessor Behavior is fixed, because there would be two
    *     Behaviors with identical names and parameters but different
    *     return values, which is not legal in Java.  In the following
    *     example, the Property Data Type cannot be changed:
    *
    *           String getN()       - accessor for Property N of type String
    *                                 and implementor of some interface
    *                                 (origin=Property,implements)
    *
    * 4.  It is possible for the Data Type to change if the in accessor
    *     Behaviors are fixed, because new Behaviors would be created with
    *     different Parameter types.  In other words, changing the Data Type
    *     attribute of the Property will create new Behaviors if the current
    *     related Behaviors have an origin of implements, dispatches, or
    *     integrates.  Example:
    *
    *       Before:
    *           void setN(String)   - accessor for Property N of type String
    *                                 and implementor of some interface
    *                                 (origin=Property,implements)
    *
    *       After Data Type changed from String to int:
    *           void setN(int)      - accessor for Property N of type int
    *                                 (origin=Property)
    *           void setN(String)   - a Behavior unrelated to the Property
    *                                 (origin=implements)
    *
    * 5.  As the corollary to #4 above, changing the Data Type such that
    *     the new signature of an in accessor Behavior is identical to an
    *     existing Behavior's signature, the Property origin is transfered
    *     from the old accessor Behavior to the new one.  In so doing, the
    *     old accessor Behavior may end up with no origin, and thus must be
    *     removed.  Example:
    *
    *       Before:
    *           void setN(int)      - accessor for Property N of type int
    *                                 (origin=Property)
    *           void setN(String)   - a Behavior unrelated to the Property
    *                                 (origin=implements)
    *
    *       After Data Type changed from int to String:
    *           void setN(String)   - a Behavior unrelated to the Property
    *                                 (origin=Property,implements)
    *
    * @return true if the data type of the Property can be set
    */
    public boolean isDataTypeSettable()
        {
        return isDeclaredAtThisLevel()
            && isModifiable()
            && !isFromIntegration()
            && isAccessorOwned(PA_GET_SINGLE)
            && isAccessorOwned(PA_GET_ARRAY )
            && isAccessorOwned(PA_GET_INDEX );
        }

    /**
    * Determine if the specified data type is a legal data type value for
    * this Property.  The isDataTypeSettable method answers the question
    * "can the type change", and this method answers the question "can it
    * change to this specific type".  Where the rules for settability are
    * based on the relation to Behaviors for the current type, the rules
    * for legality are based on the relation to Behaviors for the new type.
    *
    * Specifically, the new type is legal if there are no conflicts with
    * the Behaviors that would be reserved/related if the Property Data
    * Type changed.  This includes:
    *
    * 1.  No return value (or other attribute) conflicts.  For example, the
    *     Property Data Type could not be changed to String in the following
    *     example because the signature of the new accessor would collide
    *     with an existing Behavior that has a return value mismatch with
    *     the accessor:
    *
    *           void setN(int)      - accessor for Property N of type int
    *                                 (origin=Property)
    *           int setN(String)    - a Behavior unrelated to the Property
    *                                 (origin=implements)
    *
    *     In addition to the return value, the static and accessible
    *     attributes must match (or be matchable).
    *
    * 2.  No super origin conflicts.  For example, the Property Data Type
    *     could not be changed to String in the following example because
    *     the signature of the new accessor would collide with an existing
    *     Behavior that existed at a super level.  (No accessor Behaviors
    *     reserved by a Property can exist at a super level.)
    *
    *           void setN(int)      - accessor for Property N of type int
    *                                 (origin=Property)
    *           void setN(String)   - a Behavior unrelated to the Property
    *                                 (origin=super)
    *
    * 3.  No reserved but non-related Behaviors can exist.  For example,
    *     if the following Behaviors exists, the Data Type for Property
    *     N cannot be changed from int to String because the Property N
    *     is gettable only:
    *
    *           int getN()          - accessor for Property N of type int
    *                                 (origin=Property)
    *           void setN(String)   - a Behavior unrelated to the Property
    *                                 (origin=manual)
    *
    * @return true if the specified data type is legal for this property
    *
    * @see com.tangosol.dev.component.Property#isDataTypeSettable
    */
    public boolean isDataTypeLegal(DataType dt)
        {
        DataType VOID = DataType.VOID;
        if (dt == null || dt == VOID)
            {
            return false;
            }

        if (dt == m_dt)
            {
            return true;
            }

        Behavior[] aCurrent  = getReservedBehaviors();
        Behavior[] aBehavior = getReservedBehaviors(dt, m_sName);
        int        cBehavior = aBehavior.length;
        boolean    fIndexed  = !isSingle();
        for (int i = 0; i < cBehavior; ++i)
            {
            Behavior current  = aCurrent [i];
            Behavior behavior = aBehavior[i];
            // 1999.11.22  cp   the additional check that the behavior is not
            // from this property is necessary because if the data type is
            // changing from T to T[] for an indexed property, the old set
            // array behavior will appear to be the new set single behavior
            // (i.e. the for an indexed property P with type T, "setP([T)V"
            // is the array setter, and when changing the property type to
            // T[], it appears that the single setter already exists)
            if (behavior != null && behavior != current && !behavior.isFromTrait(this))
                {
                // assertion:  remove after testing
                // the reserved Behaviors can't be from a Property
                if (behavior.isFromProperty())
                    {
                    throw new IllegalStateException(CLASS + ".isDataTypeLegal:  " +
                        "Illegal Behavior origin!  (" + behavior.toString() + ")");
                    }

                // check if the Behavior is reserved (i.e. should not be
                // declared)
                if (current == null)
                    {
                    return false;
                    }

                // check for origin conflict
                if (behavior.isFromSuper())
                    {
                    return false;
                    }

                // make sure name matches (accessors are case-sensitive)
                if (!behavior.getName().equals(current.getName()))
                    {
                    return false;
                    }

                // check for return type conflict
                DataType dtRet    = null;
                DataType dtActual = behavior.getReturnValue().getDataType();
                switch (i)
                    {
                    case RSVD_SET_SINGLE:
                    case RSVD_SET_ARRAY:
                    case RSVD_SET_INDEX:
                        dtRet = DataType.VOID;
                        break;

                    case RSVD_GET_EITHER:
                    case RSVD_IS_EITHER:
                        dtRet = (fIndexed ? dt.getArrayType() : dt);
                        break;

                    case RSVD_GET_INDEX:
                    case RSVD_IS_INDEX:
                        dtRet = dt;
                        break;
                    }
                if (dtRet != dtActual)
                    {
                    // the Behavior has an invalid return type
                    ReturnValue retval = behavior.getReturnValue();
                    if (!(retval.isDataTypeSettable() && retval.isDataTypeLegal(dtRet)))
                        {
                        return false;
                        }
                    }

                // check for scope (static) conflict
                if (behavior.isStatic() != current.isStatic())
                    {
                    if (!behavior.isStaticSettable())
                        {
                        return false;
                        }
                    }
                }
            }

        return true;
        }

    /**
    * Set the property data type.
    *
    * @param dt  the new data type for the property
    *
    * @exception PropertyVetoException if the property cannot be set as
    *            specified or was vetoed by a property listener
    */
    public void setDataType(DataType dt)
            throws PropertyVetoException
        {
        setDataType(dt, true);
        }

    /**
    * Set the Property data type.  For each related behavior, there are
    * several possible results to changing the Data Type:
    *
    * 1.  If the Behavior signature does not change as a result of the Data
    *     Type change (i.e. out accessors) then the Behavior's return Data
    *     Type is modified:
    *
    *       Before:
    *           int getN()
    *
    *       After:
    *           String getN()
    *
    * 2.  If the change to the Property Data Type is reflected in a change
    *     to an accessor Behavior's parameter Data Type (i.e. in accessors)
    *     which therefore changes the Behavior's signature, then:
    *
    *     1.  If a Behavior already exists with the new Behavior signature,
    *         then it becomes the Property accessor.
    *
    *         1.  The new accessor is modified if necessary to match the
    *             desired Property accessor Behavior attributes.  This
    *             includes the return value, static, and accessibility
    *             attributes.
    *
    *         2.  The Property origin is removed from the previous accessor
    *             Behavior.  If this leaves the Behavior without an origin,
    *             then the behavior is removed.
    *
    *     2.  If the parameter Data Type of the existing accessor Behavior
    *         cannot be changed because the Behavior has an origin of
    *         implements, dispatches, or integrates, then a new accessor
    *         Behavior must be created with the new parameter Data Type.
    *
    *     3.  Otherwise, the existing accessor Behavior's parameter Data Type
    *         is changed.
    *
    * @param dt         the new data type for the parameter
    * @param fVetoable  true if the setter can veto the value
    *
    * @exception PropertyVetoException if the property cannot be set as
    *            specified or was vetoed by a property listener
    */
    protected synchronized void setDataType(DataType dt, boolean fVetoable)
            throws PropertyVetoException
        {
        DataType dtPrev = m_dt;

        if (dt == dtPrev)
            {
            return;
            }

        if (fVetoable)
            {
            if (!isDataTypeSettable())
                {
                readOnlyAttribute(ATTR_DATATYPE, dtPrev, dt);
                }

            if (!isDataTypeLegal(dt))
                {
                illegalAttributeValue(ATTR_DATATYPE, dtPrev, dt);
                }

            fireVetoableChange(ATTR_DATATYPE, dtPrev, dt);
            }

        // reset the design time value
        if (m_oValue != NO_VALUE)
            {
            setValue(NO_VALUE, false);
            }

        // short-cut:  constant Properties have no accessors
        if (isConstant())
            {
            m_dt = dt;
            }
        else
            {
            // get the currently related Behaviors
            Behavior[] aOldBehavior = getAccessors();

            // change the Property Data Type
            m_dt = dt;

            // find any existing Behaviors that have the signatures of
            // the Property accessors after the Data Type change
            Behavior[] aNewBehavior = getAccessors();

            // (1) change return types for "out" accessor Behaviors
            if (isGettable())
                {
                // if the type is changing to or from boolean, the accessor
                // will toggle between is/get
                boolean fPrefixChange = dt == BOOLEAN || dtPrev == BOOLEAN;

                switch (getIndexed())
                    {
                    case PROP_SINGLE:
                        aOldBehavior[PA_GET_SINGLE].getReturnValue().setDataType(dt, false);
                        if (fPrefixChange)
                            {
                            String sSig  = getAccessorSignature(PA_GET_SINGLE);
                            String sName = sSig.substring(0, sSig.indexOf('('));
                            aOldBehavior[PA_GET_SINGLE].setName(sName, false);
                            }
                        break;
                    case PROP_INDEXED:
                        aOldBehavior[PA_GET_ARRAY].getReturnValue().setDataType(dt.getArrayType(), false);
                        if (fPrefixChange)
                            {
                            String sSig  = getAccessorSignature(PA_GET_ARRAY);
                            String sName = sSig.substring(0, sSig.indexOf('('));
                            aOldBehavior[PA_GET_ARRAY].setName(sName, false);
                            }
                        // no break;
                    case PROP_INDEXEDONLY:
                        aOldBehavior[PA_GET_INDEX].getReturnValue().setDataType(dt, false);
                        if (fPrefixChange)
                            {
                            String sSig  = getAccessorSignature(PA_GET_INDEX);
                            String sName = sSig.substring(0, sSig.indexOf('('));
                            aOldBehavior[PA_GET_INDEX].setName(sName, false);
                            }
                        break;
                    }
                }

            // (2) change the Parameter types for "in" accessor Behaviors
            if (isSettable())
                {
                switch (getIndexed())
                    {
                    case PROP_SINGLE:
                        setBehaviorParameterType(PA_SET_SINGLE,
                                aOldBehavior[PA_SET_SINGLE],
                                aNewBehavior[PA_SET_SINGLE], 0, dt);
                        break;
                    case PROP_INDEXED:
                        setBehaviorParameterType(PA_SET_ARRAY,
                                aOldBehavior[PA_SET_ARRAY],
                                aNewBehavior[PA_SET_ARRAY], 0, dt.getArrayType());
                        // no break;
                    case PROP_INDEXEDONLY:
                        setBehaviorParameterType(PA_SET_INDEX,
                                aOldBehavior[PA_SET_INDEX],
                                aNewBehavior[PA_SET_INDEX], 1, dt);
                        break;
                    }
                }
            }

        firePropertyChange(ATTR_DATATYPE, dtPrev, dt);
        }

    /**
    * Set the specified Behavior's return type to the specified Data Type.
    *
    * @param nAccessor  the accessor Behavior enumerated id (PA_)
    * @param behOld     the old accessor Behavior
    * @param behNew     the new accessor Behavior (usually null)
    * @param iParam     the Parameter index to modify
    * @param dt         the new Data Type for the Parameter
    */
    protected void setBehaviorParameterType(int nAccessor, Behavior behOld, Behavior behNew, int iParam, DataType dt)
        {
        try
            {
            if (behNew != null && behNew != behOld)
                {
                // (2.1) a Behavior already exists with the new signature
                behOld.removeOriginTrait(this);
                behNew.addOriginTrait(this);

                // (2.1.1) modify new Behavior attributes as necessary to
                // become the Property accessor and transfer Property origin
                // to the new
                ReturnValue retval = behNew.getReturnValue();
                if (retval.getDataType() != DataType.VOID)
                    {
                    retval.setDataType(DataType.VOID, false);
                    }

                if (behNew.isStatic() != behOld.isStatic())
                    {
                    behNew.setStatic(behOld.isStatic(), false);
                    }

                // (2.1.2) remove previous accessor Behavior if it no longer
                // has an origin
                if (behOld.isFromNothing())
                    {
                    getComponent().removeBehavior(behOld, false);
                    }
                }
            else if (behOld.isFromImplements()  ||
                     behOld.isFromDispatches()  ||
                     behOld.isFromIntegration()  )
                {
                // (2.2) the existing accessor Behavior cannot be changed
                // to reflect the Property Data Type change because of its
                // origin, so create a new accessor Behavior
                behOld.removeOriginTrait(this);
                behNew = addAccessor(nAccessor);
                }
            else
                {
                // (2.3) change the Parameter Data Type
                behNew = behOld;
                behNew.getParameter(iParam).setDataType(dt, false);
                }
            }
        catch (PropertyVetoException e)
            {
            throw new IllegalStateException(CLASS + ".setBehaviorParameterType:  "
                    + "Unexpected Property Veto (" + e.toString() + ")");
            }
        }

    /**
    * Get an integral value that represents the data type of the Property.
    * Any unsupported design-time type is returned as DT_SERIALIZABLE,
    * which means that for a design-time value to be stored (persistable),
    * the value (if it is not null or NO_VALUE) must be an Object that
    * implements the Serializable interface.
    *
    * @return the DT_ value corresponding to the Property's Data Type
    */
    protected byte getDataTypeEnum()
        {
        return getDataTypeEnum(m_dt);
        }

    /**
    * Get an integral value that represents the data type of the Property.
    * Any unsupported design-time type is returned as DT_SERIALIZABLE,
    * which means that for a design-time value to be stored (persistable),
    * the value (if it is not null or NO_VALUE) must be an Object that
    * implements the Serializable interface.
    *
    * @param dt  the Data Type to convert to an integral value
    *
    * @return the DT_ value corresponding to the passed Data Type
    */
    protected static byte getDataTypeEnum(DataType dt)
        {
        // Java intrinsic types
        if (dt.isPrimitive())
            {
            switch (dt.getTypeString().charAt(0))
                {
                case 'Z':
                    return DT_BOOLEAN;
                case 'C':
                    return DT_CHAR;
                case 'B':
                    return DT_BYTE;
                case 'S':
                    return DT_SHORT;
                case 'I':
                    return DT_INT;
                case 'J':
                    return DT_LONG;
                case 'F':
                    return DT_FLOAT;
                case 'D':
                    return DT_DOUBLE;
                default:
                    throw new IllegalStateException(CLASS + ".getDataTypeEnum:  Invalid simple type");
                }
            }
        else if (dt == BINARY)
            {
            return DT_BINARY;
            }
        else if (dt == STRING)
            {
            return DT_STRING;
            }
        else if (dt.isComponent())
            {
            return DT_COMPLEX;
            }
        else
            {
            return DT_SERIALIZABLE;
            }
        }

    /**
    * Determine if the enumerated data type is simple.
    *
    * @return true if the type is an intrinsic Java type.
    */
    protected static boolean isDataTypeEnumSimple(byte nType)
        {
        return nType >= DT_BOOLEAN && nType <= DT_DOUBLE;
        }

    /**
    * Determine if the enumerated data type is numeric (castable to
    * java.lang.Number).
    *
    * @return true if the type is an intrinsic Java number type.
    */
    protected static boolean isDataTypeEnumNumeric(byte nType)
        {
        return nType >= DT_BYTE && nType <= DT_DOUBLE;
        }


    // ----- Name

    /**
    * Get the name of this Property.  The name of the Property uniquely
    * identifies the Property within the context of the Component Definition.
    *
    * @return the name of this Property
    */
    public String getName()
        {
        return m_sName;
        }

    /**
    * Determine if the property name can be set.  The Property name can
    * be modified if all of the following are true:
    *
    * 1.  The Property is modifiable
    * 2.  The Property is declared at this level
    *
    * @return true if the Property name can be modified
    */
    public boolean isNameSettable()
        {
        return isDeclaredAtThisLevel()
            && isModifiable()
            && !isFromIntegration();
        }

    /**
    * Determine if this Property's name can be changed to the specified name.
    * The specified name is legal if all of the following are true:
    *
    * 1.  The name is a legal Property name
    * 2.  The name is not a reserved Property name
    * 3.  No conflicts exist with any reserved Behaviors for the new Property
    *     name
    *
    * @return true if the specified name is legal for this property
    */
    public boolean isNameLegal(String sName)
        {
        if (sName.equals(m_sName))
            {
            return true;
            }
        else if (INSENS.equals(sName, m_sName))
            {
            // case change, all accessors must be owned
            for (int i = PA_FIRST; i <= PA_LAST; ++i)
                {
                if (!isAccessorOwned(i))
                    {
                    return false;
                    }
                }
            return true;
            }
        else
            {
            return isCreatable(getComponent(), m_dt, sName, getIndexed(),
                isStatic(), getDirection(), isConstant());
            }
        }

    /**
    * Set the property name.
    *
    * @param sName the new name for the property
    *
    * @exception PropertyVetoException if the new attribute value is not
    *            accepted
    */
    public void setName(String sName)
            throws PropertyVetoException
        {
        setName(sName, true);
        }

    /**
    * Change the name attribute of the Property.  This involves changing the
    * name of each associated Behavior or, if a collision occurs, destroying
    * the existing accessor Behavior and transfering origin to the Behavior
    * which already has the necessary signature.
    *
    * @param sName      the new name for the property
    * @param fVetoable  true if the setter can veto the value
    *
    * @exception PropertyVetoException if the new attribute value is not
    *            accepted
    */
    protected synchronized void setName(String sName, boolean fVetoable)
            throws PropertyVetoException
        {
        String sPrev = m_sName;

        if (sName.equals(sPrev))
            {
            return;
            }

        if (fVetoable)
            {
            if (!isNameSettable())
                {
                readOnlyAttribute(ATTR_NAME, sPrev, sName);
                }

            if (!isNameLegal(sName))
                {
                illegalAttributeValue(ATTR_NAME, sPrev, sName);
                }

            fireVetoableChange(ATTR_NAME, sPrev, sName);
            }

        // get the current accessors
        Behavior[] aBehOld = getAccessors();

        // remove the property as an origin from those accessors;
        // it won't be possible to remove after the property name
        // changes
        for (int i = PA_FIRST; i <= PA_LAST; ++i)
            {
            Behavior behOld = aBehOld[i];
            if (behOld != null)
                {
                behOld.removeOriginTrait(this);
                }
            }

        getComponent().renameProperty(sPrev, sName);
        m_sName = sName;

        // case change (x becomes X)
        if (INSENS.equals(sName, sPrev))
            {
            for (int i = PA_FIRST; i <= PA_LAST; ++i)
                {
                Behavior behOld = aBehOld[i];
                if (behOld != null)
                    {
                    String sSig = getAccessorSignature(i);
                    String sBeh = sSig.substring(0, sSig.indexOf('('));
                    behOld.setName(sBeh, false);
                    behOld.addOriginTrait(this);
                    }
                }
            }
        else
            {
            // get any pre-existing new accessors (i.e. collisions)
            Behavior[] aBehNew = getAccessors();

            for (int i = PA_FIRST; i <= PA_LAST; ++i)
                {
                Behavior behOld = aBehOld[i];
                Behavior behNew = aBehNew[i];
                if (behOld != null)
                    {
                    if (behNew == null)
                        {
                        // there is no Behavior that has the signature
                        // of the accessor for the renamed Property, so
                        // just rename the old accessor if it is owned
                        // by this property (i.e. no other trait lays
                        // claim to it)
                        if (isAccessorOwned(aBehOld[i]))
                            {
                            // determine new accessor name
                            String sSig = getAccessorSignature(i);
                            String sBeh = sSig.substring(0, sSig.indexOf('('));

                            // rename old accessor
                            behOld.setName(sBeh, false);
                            behOld.addOriginTrait(this);
                            }
                        else
                            {
                            // create new accessor (unable to rename old)
                            behNew = addAccessor(i);
                            }
                        }
                    else
                        {
                        // setName in case of case sensitivity problem
                        // 1999.11.15  cp   The above comment appears to mean
                        // that a potential problem exists when changing a
                        // property from X to Y when a method such as gety
                        // already exists; in such a case, the above comment
                        // implies that calling setName against the behavior
                        // (with vetoable=false) will cure this potential disaster.
                        String sSig = getAccessorSignature(i);
                        String sBeh = sSig.substring(0, sSig.indexOf('('));
                        behNew.setName(sBeh, false);

                        // a Behavior already exists with the signature
                        // of the accessor for the renamed Property;
                        // modify new Behavior attributes as necessary
                        // to become the Property accessor
                        ReturnValue retvalOld = behOld.getReturnValue();
                        ReturnValue retvalNew = behNew.getReturnValue();
                        if (retvalNew.getDataType() != retvalOld.getDataType())
                            {
                            retvalNew.setDataType(retvalOld.getDataType(), false);
                            }

                        if (behNew.isStatic() != behOld.isStatic())
                            {
                            behNew.setStatic(behOld.isStatic(), false);
                            }

                        behNew.addOriginTrait(this);

                        // remove the previous accessor Behavior if it
                        // no longer has an origin
                        if (behOld.isFromNothing())
                            {
                            getComponent().removeBehavior(behOld, false);
                            }
                        }
                    }
                }
            }

        firePropertyChange(ATTR_NAME, sPrev, sName);
        }


    // ----- Indexed

    /**
    * Determine if the property is single (not indexed).
    *
    * @return true if the Property is single or false if it is indexed
    */
    public boolean isSingle()
        {
        return (m_nFlags & PROP_MASK) == PROP_SINGLE;
        }

    /**
    * Determine the value of the Indexed attribute of the Property.
    *
    * @return one of PROP_SINGLE, PROP_INDEXED, or PROP_INDEXEDONLY
    */
    public int getIndexed()
        {
        return m_nFlags & PROP_MASK;
        }

    /**
    * Determine if the indexed attribute of the Property can be set.
    * The indexed attribute cannot be modified if any of the behaviors that
    * implement the property accessors are from interfaces, or other sources,
    * because changing the indexed attribute changes the Java data type that
    * is used to implement the property.  For example:
    *
    * property declaration          accessor (get)      accessor (set)
    * ----------------------------  ------------------  ---------------------
    * property int x                int getX()          void setX(int)
    * property indexed int x        int[] getX()        void setX(int[])
    *
    * @return true if the indexed attribute of the property can be set
    */
    public boolean isIndexedSettable()
        {
        if (isDeclaredAtThisLevel() && isModifiable() && !isFromIntegration())
            {
            if (isConstant())
                {
                return true;
                }

            switch (getIndexed())
                {
                case PROP_SINGLE:
                    // single value Properties cannot be changed (to either
                    // form of indexed) if the out accessor Behaviors have
                    // an origin of implements, dispatches, or integrates
                    return isAccessorOwned(PA_GET_SINGLE);

                case PROP_INDEXED:
                    // indexed Properties cannot be changed (to either single
                    // or indexed only) if the array out accessors have an
                    // origin of implements, dispatches, or integrates
                    return isAccessorOwned(PA_GET_ARRAY);

                case PROP_INDEXEDONLY:
                    // indexed-only can be changed to indexed without modifying any
                    // Behaviors
                    return true;

                // assertion
                default:
                    throw new IllegalStateException(CLASS + ".isIndexedSettable");
                }
            }

        return false;
        }

    /**
    * Determine if the indexed attribute of the Property can be set to the
    * specified value.  Changing the indexed attribute can result in:
    *
    *   1.  Destruction of Behaviors
    *   2.  Creation of Behaviors
    *   3.  Modification of Behavior Parameters and Return Values
    *
    * Issue #2 is not a problem since additional Behaviors can be created
    * without a problem (since they are reserved).
    *
    * @param nIndexed the proposed value for the Property indexed attribute
    *
    * @return true if the indexed attribute of the property can be set to
    *          the specified value
    */
    public boolean isIndexedLegal(int nIndexed)
        {
        // validate domain
        switch (nIndexed)
            {
            case PROP_SINGLE:
            case PROP_INDEXED:
            case PROP_INDEXEDONLY:
                break;
            default:
                return false;
            }

        // short-cut:  check for no change
        int nPrev = getIndexed();
        if (nIndexed == nPrev)
            {
            return true;
            }

        // Java constants (i.e. a Java field) cannot be indexed only
        // virtual constants can be any (single, indexed or indexed only)
        if (isConstant())
            {
            return nIndexed != PROP_INDEXEDONLY || isVirtualConstant();
            }

        switch (nPrev)
            {
            case PROP_SINGLE:
                // PROP_INDEXED:      change single to array accessors
                // PROP_INDEXEDONLY:  change single to indexed accessors
                return isAccessorOwned(PA_GET_SINGLE) &&
                       isAccessorOwned(PA_SET_SINGLE);

            case PROP_INDEXED:
                // PROP_SINGLE:  change array to single accessors and drop
                //               indexed accessors
                if (nIndexed == PROP_SINGLE)
                    {
                    return isAccessorOwned    (PA_GET_ARRAY) &&
                           isAccessorOwned    (PA_SET_ARRAY) &&
                           isAccessorRemovable(PA_GET_INDEX) &&
                           isAccessorRemovable(PA_SET_INDEX);
                    }

                // PROP_INDEXEDONLY:  drop array accessors
                return isAccessorRemovable(PA_GET_ARRAY) &&
                       isAccessorRemovable(PA_SET_ARRAY);

            case PROP_INDEXEDONLY:
                // PROP_SINGLE:  change indexed to single accessors
                if (nIndexed == PROP_SINGLE)
                    {
                    return isAccessorOwned(PA_GET_INDEX) &&
                           isAccessorOwned(PA_SET_INDEX);
                    }

                // PROP_INDEXED:  no accessor drops or changes
                return true;

            // assertion
            default:
                throw new IllegalStateException(CLASS + ".isIndexedLegal");
            }
        }

    /**
    * Change the indexed attribute of the Property.
    *
    * @param nIndexed  the new indexed attribute for the Property
    *
    * @exception PropertyVetoException if the new attribute value is not
    *            accepted
    */
    public void setIndexed(int nIndexed)
            throws PropertyVetoException
        {
        setIndexed(nIndexed, true);
        }

    /**
    * Change the indexed attribute of the Property.  Depending on the
    * previous and new value of the indexed attribute, the actions taken
    * will include:
    *
    *   1.  Destruction of accessor Behaviors
    *   2.  Creation of accessor Behaviors
    *   3.  Modification of accessor Behavior Parameters and Return Values
    *
    * @param nIndexed  the new indexed attribute for the Property
    * @param fVetoable  true if the setter can veto the value
    *
    * @exception PropertyVetoException if the new attribute value is not
    *            accepted
    */
    protected synchronized void setIndexed(int nIndexed, boolean fVetoable)
            throws PropertyVetoException
        {
        int nPrev = getIndexed();

        if (nIndexed == nPrev)
            {
            return;
            }

        Integer prev  = nPrev;
        Integer value = nIndexed;

        if (fVetoable)
            {
            if (!isIndexedSettable())
                {
                readOnlyAttribute(ATTR_INDEXED, prev, value);
                }

            if (!isIndexedLegal(nIndexed))
                {
                illegalAttributeValue(ATTR_INDEXED, prev, value);
                }

            fireVetoableChange(ATTR_INDEXED, prev, value);
            }

        m_nFlags = m_nFlags & ~PROP_MASK | nIndexed;

        // reset the design time value
        if (m_oValue != NO_VALUE)
            {
            setValue(NO_VALUE, false);
            }

        if (!isConstant())
            {
            // create, modify, and destroy behaviors to reflect the indexed
            // attribute change
            DataType dtSingle  = m_dt;
            DataType dtArray   = dtSingle.getArrayType();
            boolean  fGettable = isGettable();
            boolean  fSettable = isSettable();
            switch (nPrev)
                {
                case PROP_SINGLE:
                    if (nIndexed == PROP_INDEXED)
                        {
                        // PROP_INDEXED:
                        // change single to array accessors
                        if (fGettable)
                            {
                            // T getN() -> T[] getN()
                            getAccessor(PA_GET_SINGLE).getReturnValue().setDataType(dtArray, false);
                            }
                        if (fSettable)
                            {
                            // void setN(T) -> void setN(T[])
                            getAccessor(PA_SET_SINGLE).getParameter(0).setDataType(dtArray, false);
                            }

                        // create indexed accessors
                        if (fGettable)
                            {
                            addAccessor(PA_GET_INDEX);
                            }
                        if (fSettable)
                            {
                            addAccessor(PA_SET_INDEX);
                            }
                        }
                    else
                        {
                        // PROP_INDEXEDONLY:
                        // change single to indexed accessors
                        if (fGettable)
                            {
                            // T getN() -> T getN(int)
                            getAccessor(PA_GET_SINGLE).addParameter(-1, INT, INDEX_PARAM, false);
                            }
                        if (fSettable)
                            {
                            // void setN(T) -> void setN(int, T)
                            getAccessor(PA_SET_SINGLE).addParameter(0, INT, INDEX_PARAM, false);
                            }
                        }
                    break;

                case PROP_INDEXED:
                    if (nIndexed == PROP_SINGLE)
                        {
                        // PROP_SINGLE:
                        // change array to single accessors
                        if (fGettable)
                            {
                            // T[] getN() -> T getN()
                            getAccessor(PA_GET_ARRAY).getReturnValue().setDataType(dtSingle, false);
                            }
                        if (fSettable)
                            {
                            // void setN(T[]) -> void setN(T)
                            getAccessor(PA_SET_ARRAY).getParameter(0).setDataType(dtSingle, false);
                            }

                        // drop indexed accessors
                        if (fGettable)
                            {
                            removeAccessor(PA_GET_INDEX);
                            }
                        if (fSettable)
                            {
                            removeAccessor(PA_SET_INDEX);
                            }
                        }
                    else
                        {
                        // PROP_INDEXEDONLY:
                        // drop array accessors
                        if (fGettable)
                            {
                            removeAccessor(PA_GET_ARRAY);
                            }
                        if (fSettable)
                            {
                            removeAccessor(PA_SET_ARRAY);
                            }
                        }
                    break;

                case PROP_INDEXEDONLY:
                    if (nIndexed == PROP_SINGLE)
                        {
                        // PROP_SINGLE:
                        // change indexed to single accessors
                        if (fGettable)
                            {
                            // T getN(int) -> T getN()
                            getAccessor(PA_GET_INDEX).removeParameter(0, false);
                            }
                        if (fSettable)
                            {
                            // void setN(int, T) -> void setN(T)
                            getAccessor(PA_SET_INDEX).removeParameter(0, false);
                            }
                        }
                    else
                        {
                        // PROP_INDEXED:
                        // create array accessors
                        if (fGettable)
                            {
                            addAccessor(PA_GET_ARRAY);
                            }
                        if (fSettable)
                            {
                            addAccessor(PA_SET_ARRAY);
                            }
                        }
                    break;

                // assertion
                default:
                    throw new IllegalStateException(CLASS + ".setIndexed");
                }
            }

        firePropertyChange(ATTR_INDEXED, prev, value);
        }


    // ----- Value

    /**
    * Determine if the Property has no design-time value.
    *
    * @return true if the Property has no value, false otherwise
    */
    public boolean isNoValue()
        {
        return m_oValue == NO_VALUE;
        }

    /**
    * Determine if the Property derivation/modification has no value delta.
    *
    * @return true if the Property has no value delta, false otherwise
    */
    protected boolean isNoDelta()
        {
        return m_oValue == NO_DELTA;
        }

    /**
    * Determine if the design-time value of the Property is null.
    *
    * @return true if the Property value is null, false otherwise
    */
    public boolean isNullValue()
        {
        return m_oValue == null;
        }

    /**
    * Get the value of this Property.  This operation is optimized since
    * Property values will be "gotten" all of the time to support tools
    * such as Property sheets and visual layout editors.
    *
    * @return  the current design-time value for this Property
    */
    public Object getValue()
        {
        // clone array values (so that caller doesn't hold a reference
        // to the Property's internal value)
        Object oValue = m_oValue;
        if (oValue instanceof Object[])
            {
            return ((Object[]) oValue).clone();
            }
        else
            {
            return oValue;
            }
        }

    /**
    * Determine if the design-time value of the Property is the
    * same as its base/super value.
    *
    * @return true if the Property value is null, false otherwise
    */
    public boolean isValueDiscardable()
        {
        return isValueEqual(m_oPrevValue);
        }

    /**
    * Get the previous (default) value of this Property.
    *
    * @return  the super/base design-time value for this Property
    */
    public Object getDefaultValue()
        {
        // clone array values (so that caller doesn't hold a reference
        // to the Property's internal value)
        Object oValue = m_oPrevValue;
        if (oValue instanceof Object[])
            {
            return ((Object[]) oValue).clone();
            }
        else
            {
            return oValue;
            }
        }

    /**
    * Determine if the design-time value of this Property can be set.
    *
    * Assuming the Property is modifiable, the value is settable except for
    * the following exceptions:
    *
    *   1.  Java constant - only settable at the derivation level that
    *       the Property was declared at
    *   2.  Calculated or IndexedOnly Property - not settable (no way to initialize)
    *
    * @return true if the value of this Property can be set
    */
    public boolean isValueSettable()
        {
        if (!isModifiable())
            {
            return false;
            }

        // gg: 2001.8.22 JCS property values are not settable for now
        if (getComponent().isSignature())
            {
            return false;
            }

        // all properties present in a complex have modifiable values
        if (isComponentComplex())
            {
            return true;
            }

        // we currently do not support setting arrays
        // of component complex properties...
        DataType dt = m_dt;
        if (dt.isArray())
            {
            if (dt.getBaseElementType().isComponent())
                {
                return false;
                }
            }

        if (isVirtualConstant())
            {
            // virtual constants are settable at every level
            return true;
            }

        if (isCalculatedProperty())
            {
            // calculated Property values are determined at runtime via
            // code; a calculated Property has no setter accessor(s) and
            // so the value cannot be set
            return false;
            }

        if (isJavaConstant())
            {
            // Java constants are not settable if the Property is
            // integrated or declared at a super level (i.e. if the
            // resulting field is in a super class)
            return !isFromSuper() && !isFromIntegration();
            }

        if (isStatic())
            {
            // Java static properties are not settable if the Property
            // is declared at a super level (i.e. if the resulting field
            // is in a super class)
            return !isFromSuper();
            }

        // standard or functional instance Property

        // if the Property is single or indexed, then the "regular"
        // setter must be available;
        Behavior setter;
        switch (getIndexed())
            {
            case PROP_SINGLE:
                setter = getAccessor(PA_SET_SINGLE);
                break;
            case PROP_INDEXED:
                setter = getAccessor(PA_SET_ARRAY);
                break;
            case PROP_INDEXEDONLY:
                // indexed-only properties cannot have a design-time
                // value (there is no "storage" i.e. field)
                return false;
            default:
                throw new IllegalStateException(CLASS + ".isValueSettable");
            }

        // the setter must be available at this level
        if (setter == null)
            {
            return false;
            }

        return true;
        }

    /**
    * Determine if the specified value is legal for the design-time value of
    * this Property to be set to.
    *
    * @return true if the value of this Property can be set to the specified
    *         value
    */
    public boolean isValueLegal(Object oValue)
        {
        DataType dt = m_dt;
        int nIndexed = getIndexed();
        if (nIndexed == PROP_SINGLE)
            {
            return isValueLegal(dt, oValue);
            }
        else
            {
            if (oValue == null || oValue == NO_VALUE)
                {
                // the "array" of index-only Properties can't be set to null
                // or no-value because there is no array setter
                return (nIndexed == PROP_INDEXED);
                }

            if (!(oValue instanceof Object[]))
                {
                return false;
                }

            Object[] aoValue = (Object[]) oValue;
            int      cValues = aoValue.length;
            for (int i = 0; i < cValues; ++i)
                {
                if (!isValueLegal(dt, aoValue[i]))
                    {
                    return false;
                    }
                }

            return true;
            }
        }

    /**
    * Determine if the specified value is equal to the current value.
    *
    * @return true if the values are equal
    */
    public boolean isValueEqual(Object oNewValue)
        {
        return isValueEqual(m_dt, isSingle(), m_oValue, oNewValue);
        }

    /**
    * Set the design-time value of this Property.
    *
    * @param oValue  the new value for this Property
    *
    * @exception PropertyVetoException if the new attribute value is not
    *            accepted
    */
    public void setValue(Object oValue)
            throws PropertyVetoException
        {
        setValue(oValue, true);
        }

    /**
    * Set the design-time value of this Property.
    *
    * @param oValue     the new value for this Property
    * @param fVetoable  true if the setter can veto the value
    *
    * @exception PropertyVetoException if the new attribute value is not
    *            accepted
    */
    protected synchronized void setValue(Object oValue, boolean fVetoable)
            throws PropertyVetoException
        {
        if (isValueEqual(oValue))
            {
            return;
            }
        Object oPrev = m_oValue;

        if (fVetoable)
            {
            if (!isValueSettable())
                {
                readOnlyAttribute(ATTR_VALUE, oPrev, oValue);
                }

            if (!isValueLegal(oValue))
                {
                illegalAttributeValue(ATTR_VALUE, oPrev, oValue);
                }

            fireVetoableChange(ATTR_VALUE, oPrev, oValue);
            }

        // assume the value is good as is...
        m_oValue = oValue;

        // clone array values (so that caller doesn't hold a reference
        // to the Property's internal value)
        // since we could put a NO_DELTA there and what is being passed
        // doesn't have to be an array of Objects, but could be an array
        // of a more specific object type, don't use "clone()", but
        // "arraycopy()" instead
        // (or even better call some helper for each element)
        if (!isSingle() && oValue instanceof Object[])
            {
            Object[] aoValue = (Object[]) oValue;
            int      cValues = aoValue.length;
            m_oValue = new Object[cValues];
            System.arraycopy(aoValue, 0, (Object[]) m_oValue, 0, cValues);

            // deal with any complex property element values
            if (isComplex())
                {
                aoValue = (Object[]) m_oValue;
                for (int i = 0; i < aoValue.length; ++i)
                    {
                    Object oElement = aoValue[i];
                    if (oElement != NO_VALUE && oElement != null)
                        {
                        aoValue[i] = new Component(this, (Component) oElement);
                        }
                    }
                }
            }
        else if (isComplex() && oValue != NO_VALUE && oValue != null)
            {
            m_oValue = new Component(this, (Component) oValue);
            }

        firePropertyChange(ATTR_VALUE, oPrev, m_oValue);
        }

    /**
    * Reset the design-time value of this Property to its default value.
    *
    * @exception PropertyVetoException if the property cannot be set as
    *            specified or was vetoed by a property listener
    */
    public synchronized void resetValue()
            throws PropertyVetoException
        {
        setValue(m_oPrevValue);
        }

    /**
    * Get the number of value of this indexed Property.
    *
    * @return  the number of design-time indexed values
    */
    public int getValueCount()
        {
        if (isSingle() || m_oValue == null || m_oValue == NO_VALUE)
            {
            throw new IllegalStateException(CLASS + ".getValueCount:  " +
                "Property value is not an array!");
            }

        return ((Object[]) m_oValue).length;
        }

    /**
    * Set the number of value of this indexed Property.
    *
    * @param cValues  the new number of design-time indexed values
    *
    * @exception PropertyVetoException if the property cannot be set as
    *            specified or was vetoed by a property listener
    */
    public void setValueCount(int cValues)
            throws PropertyVetoException
        {
        if (isSingle())
            {
            throw new IllegalStateException(CLASS + ".setValueCount:  " +
                "Property is not indexed!");
            }

        Object   oPrev      = m_oValue;
        Object[] aoOldValue = (oPrev instanceof Object[] ? (Object[]) oPrev : NO_OBJECTS);
        Object[] aoNewValue = new Object[cValues];

        // keep whatever old values there are (or whatever will still fit)
        int cOldValues = aoOldValue.length;
        if (cOldValues > 0)
            {
            System.arraycopy(aoOldValue, 0, aoNewValue, 0, cOldValues);
            }

        // initialize any new values
        for (int i = cOldValues; i < cValues; ++i)
            {
            aoNewValue[i] = NO_VALUE;
            }

        setValue(aoNewValue);
        }

    /**
    * Get a particular value of this Property.
    *
    * @param iValue  the 0-based index of the value to get
    *
    * @return  the current design-time value for this Property
    *
    * @exception IllegalStateException if the value is not an array type
    * @exception ArrayIndexOutOfBoundsException if the index is out of range
    */
    public Object getValue(int iValue)
        {
        try
            {
            return ((Object[]) m_oValue) [iValue];
            }
        catch (ClassCastException e)
            {
            throw new IllegalStateException(CLASS + ".getValue:  " +
                "Property value is not an array!  (" + e.toString() + ")");
            }
        }

    /**
    * Set a particular design-time value of this indexed Property.
    *
    * @param iValue  the index of the value being set
    * @param oValue  the new value for this Property
    *
    * @exception IllegalStateException if the value is not an array type
    * @exception ArrayIndexOutOfBoundsException if the index is out of range
    * @exception PropertyVetoException if the property cannot be set as
    *            specified or was vetoed by a property listener
    */
    public void setValue(int iValue, Object oValue)
            throws PropertyVetoException
        {
        try
            {
            Object[] aoValue = (Object[]) getValue();
            aoValue[iValue]  = oValue;
            setValue(aoValue);
            }
        catch (ClassCastException e)
            {
            throw new IllegalStateException(CLASS + ".setValue:  " +
                "Property value is not an array!  (" + e.toString() + ")");
            }
        }


    /**
    * Add a new design-time value to this indexed Property.
    *
    * @param iValue  the index of the value being added (-1 to append)
    *
    * @exception IllegalStateException if the value is not an array type and
    *            an attempt is made to insert a value; (it is possible to
    *            append a value to an indexed Property whose value is not
    *            currently an array type)
    * @exception ArrayIndexOutOfBoundsException if the index is out of range
    * @exception PropertyVetoException if the property cannot be added as
    *            specified or was vetoed by a property listener
    */
    public void addValue(int iValue)
            throws PropertyVetoException
        {
        addValue(iValue, NO_VALUE);
        }

    /**
    * Add a particular design-time value to this indexed Property.
    *
    * @param iValue  the index of the value being added (-1 to append)
    * @param oValue  the value to add to this Property
    *
    * @exception IllegalStateException if the value is not an array type and
    *            an attempt is made to insert a value; (it is possible to
    *            append a value to an indexed Property whose value is not
    *            currently an array type)
    * @exception ArrayIndexOutOfBoundsException if the index is out of range
    * @exception PropertyVetoException if the property cannot be added as
    *            specified or was vetoed by a property listener
    */
    public void addValue(int iValue, Object oValue)
            throws PropertyVetoException
        {
        if (iValue == -1)
            {
            try
                {
                Object[] aoOldValue = (Object[]) m_oValue;
                int      cOldValues = aoOldValue.length;

                int      cNewValues = cOldValues + 1;
                Object[] aoNewValue = new Object[cNewValues];

                if (cOldValues > 0)
                    {
                    System.arraycopy(aoOldValue, 0, aoNewValue, 0, cOldValues);
                    }

                aoNewValue[cOldValues] = NO_VALUE;
                setValue(aoNewValue);
                }
            catch (ClassCastException e)
                {
                Object[] aoNewValue = new Object[1];
                aoNewValue[0] = NO_VALUE;
                setValue(aoNewValue);
                }
            }
        else
            {
            try
                {
                Object[] aoOldValue = (Object[]) m_oValue;
                int      cOldValues = aoOldValue.length;

                if (iValue < 0 || iValue > cOldValues)
                    {
                    throw new ArrayIndexOutOfBoundsException(CLASS + ".addValue:  " + iValue);
                    }

                int      cNewValues = cOldValues + 1;
                Object[] aoNewValue = new Object[cNewValues];

                if (iValue > 0)
                    {
                    System.arraycopy(aoOldValue, 0, aoNewValue, 0, iValue);
                    }

                if (iValue < cOldValues)
                    {
                    System.arraycopy(aoOldValue, iValue, aoNewValue, iValue + 1, cOldValues - iValue);
                    }

                aoNewValue[iValue] = oValue;
                setValue(aoNewValue);
                }
            catch (ClassCastException e)
                {
                throw new IllegalStateException(CLASS + ".addValue:  " +
                    "Property value is not an array!  (" + e.toString() + ")");
                }
            }
        }

    /**
    * Remove a particular design-time value from this indexed Property.
    *
    * @param iValue  the index of the value being removed (0 to count-1)
    *
    * @exception IllegalStateException if the value is not an array type
    * @exception ArrayIndexOutOfBoundsException if the index is out of range
    * @exception PropertyVetoException if the property cannot be removed as
    *            specified or was vetoed by a property listener
    */
    public void removeValue(int iValue)
            throws PropertyVetoException
        {
        removeValues(iValue, 1);
        }

    /**
    * Remove one or more design-time values from this indexed Property.
    *
    * @param iValue   the index of the first value to remove
    * @param cValues  the number of values to remove
    *
    * @exception IllegalStateException if the value is not an array type
    * @exception ArrayIndexOutOfBoundsException if the index is out of range
    * @exception PropertyVetoException if the property cannot be removed as
    *            specified or was vetoed by a property listener
    */
    public void removeValues(int iValue, int cValues)
            throws PropertyVetoException
        {
        if (cValues == 0)
            {
            return;
            }

        try
            {
            Object[] aoOldValue = (Object[]) m_oValue;
            int      cOldValues = aoOldValue.length;
            if (iValue < 0 || cValues < 0 || iValue + cValues - 1 > cOldValues)
                {
                throw new ArrayIndexOutOfBoundsException(CLASS + ".removeValues:  " + iValue + ", " + cValues);
                }

            int      cNewValues = cOldValues - cValues;
            Object[] aoNewValue = new Object[cNewValues];

            if (iValue > 0)
                {
                System.arraycopy(aoOldValue, 0, aoNewValue, 0, iValue);
                }

            if (iValue + cValues < cOldValues)
                {
                System.arraycopy(aoOldValue, iValue + cValues, aoNewValue, iValue, cOldValues - iValue - cValues);
                }

            setValue(aoNewValue);
            }
        catch (ClassCastException e)
            {
            throw new IllegalStateException(CLASS + ".removeValues:  " +
                "Property value is not an array!  (" + e.toString() + ")");
            }
        }

    /**
    * Determine if the specified single value is legal for the specified
    * Data Type.
    *
    * @param dt
    * @param oValue
    *
    * @return true if the value is legal
    */
    protected boolean isValueLegal(DataType dt, Object oValue)
        {
        byte nType = getDataTypeEnum(dt);

        // "no value" is legal for all types
        if (oValue == NO_VALUE)
            {
            return true;
            }

        // null is legal for all reference types
        if (oValue == null)
            {
            return !isDataTypeEnumSimple(nType);
            }

        switch (nType)
            {
            case DT_BOOLEAN:
                return oValue instanceof Boolean;

            case DT_CHAR:
                return oValue instanceof Character;

            case DT_BYTE:
            case DT_SHORT:
            case DT_INT:
            case DT_LONG:
            case DT_FLOAT:
            case DT_DOUBLE:
                return oValue instanceof Number;

            case DT_BINARY:
                return oValue instanceof byte[];

            case DT_STRING:
                return oValue instanceof String;

            case DT_COMPLEX:
                {
                // make sure this Property is designable
                if (!isComplexDesignable())
                    {
                    return false;
                    }

                // verify that the value is a Component
                if (!(oValue instanceof Component))
                    {
                    return false;
                    }

                // it must be a complex property component
                Component cd = (Component) oValue;
                if (!cd.isComplex())
                    {
                    return false;
                    }

                // get fully qualified Component name
                String sComponent = cd.getQualifiedName();

                // verify that the Component meets the Property data type
                // constraint
                if (!Component.isDerivedFrom(sComponent, dt.getComponentName()))
                    {
                    return false;
                    }

                // get the Component which contains this Property
                cd = getComponent();
                while (true)
                    {
                    // the complex Property value cannot derive from the
                    // containing Component
                    if (Component.isDerivedFrom(sComponent, cd.getQualifiedName()))
                        {
                        return false;
                        }

                    // it is possible that the containing Component is itself
                    // a complex Property value
                    if (!cd.isComplex())
                        {
                        return true;
                        }

                    // get the Property that contains the complex value (cd)
                    Property prop = cd.getComplex();

                    // check if this complex value is contain by a property
                    // if it is not contained by a property, then it is a
                    // free floating complex value
                    if (prop == null)
                        {
                        return true;
                        }

                    // get the Component that contains that Property
                    // (repeat the derivation check until we find a cd which
                    // is not a complex value or we are not contained by
                    // a property)
                    cd = prop.getComponent();
                    }
                }

            case DT_SERIALIZABLE:
            default:
                // it's up to a tool to decide what to store...
                return oValue instanceof Serializable;
            }
        }

    /**
    * Determine if the two specified values are equal.
    *
    * @param dt
    * @param oValue1
    * @param oValue2
    *
    * @return true if the values are equal
    */
    protected static boolean isValueEqual(DataType dt, boolean fSingle, Object oValue1, Object oValue2)
        {
        if (oValue1 == oValue2)
            {
            return true;
            }

        if (oValue1 == NO_VALUE || oValue1 == null ||
            oValue2 == NO_VALUE || oValue2 == null   )
            {
            // (already checked for equal references)
            return false;
            }

        if (fSingle)
            {
            return isValueEqual(dt, oValue1, oValue2);
            }
        else
            {
            Object[] aoValue1 = (Object[]) oValue1;
            Object[] aoValue2 = (Object[]) oValue2;

            int cValue1 = aoValue1.length;
            int cValue2 = aoValue2.length;
            if (cValue1 != cValue2)
                {
                return false;
                }

            for (int i = 0; i < cValue2; ++i)
                {
                if (!isValueEqual(dt, aoValue1[i], aoValue2[i]))
                    {
                    return false;
                    }
                }

            return true;
            }
        }

    /**
    * Determine if the specified single value is legal for the specified
    * Data Type.
    *
    * @param dt
    * @param oValue1
    * @param oValue2
    *
    * @return true if the value is legal
    */
    protected static boolean isValueEqual(DataType dt, Object oValue1, Object oValue2)
        {
        // check for equal references
        if (oValue1 == oValue2)
            {
            return true;
            }

        // check special values
        if (oValue1 == NO_VALUE || oValue1 == NO_DELTA || oValue1 == null ||
            oValue2 == NO_VALUE || oValue2 == NO_DELTA || oValue2 == null   )
            {
            // (already checked for equal references)
            return false;
            }

        if (dt == BINARY)
            {
            // byte array comparison
            byte[] ab1 = (byte[]) oValue1;
            byte[] ab2 = (byte[]) oValue2;

            int cb1 = ab1.length;
            int cb2 = ab2.length;
            if (cb1 != cb2)
                {
                return false;
                }

            int i = cb1;
            while (i-- != 0)
                {
                if (ab1[i] != ab2[i])
                    {
                    return false;
                    }
                }

            return true;
            }
        else if (dt.isPrimitive() && dt != BOOLEAN && dt != CHAR)
            {
            Number num1 = (Number) oValue1;
            Number num2 = (Number) oValue2;
            switch (getDataTypeEnum(dt))
                {
                case DT_BYTE:
                    return num1.byteValue()   == num2.byteValue();
                case DT_SHORT:
                    return num1.shortValue()  == num2.shortValue();
                case DT_INT:
                    return num1.intValue()    == num2.intValue();
                case DT_LONG:
                    return num1.longValue()   == num2.longValue();
                case DT_FLOAT:
                    return Float.floatToIntBits(num1.floatValue()) ==
                           Float.floatToIntBits(num2.floatValue());
                case DT_DOUBLE:
                    return Double.doubleToLongBits(num1.doubleValue()) ==
                           Double.doubleToLongBits(num2.doubleValue());
                default:
                    throw new IllegalStateException(CLASS + ".isValueEqual:  " +
                        "Unexpected Data Type " + dt);
                }
            }
        else if (dt.isArray())
            {
            try
                {
                DataType dtElement = dt.getElementType();

                if (oValue1 instanceof Object[])
                    {
                    Object[] aoValue1 = (Object[]) oValue1;
                    Object[] aoValue2 = (Object[]) oValue2;
                    int cValues = aoValue1.length;
                    if (cValues != aoValue2.length)
                        {
                        return false;
                        }
                    for (int i = 0; i < cValues; ++i)
                        {
                        if (!isValueEqual(dtElement, aoValue1[i], aoValue2[i]))
                            {
                            return false;
                            }
                        }
                    return true;
                    }
                else
                    {
                    switch (getDataTypeEnum(dtElement))
                        {
                        case DT_BOOLEAN:
                            return Arrays.equals((boolean[]) oValue1, (boolean[]) oValue2);
                        case DT_CHAR:
                            return Arrays.equals((char   []) oValue1, (char   []) oValue2);
                        case DT_BYTE:
                            return Arrays.equals((byte   []) oValue1, (byte   []) oValue2);
                        case DT_SHORT:
                            return Arrays.equals((short  []) oValue1, (short  []) oValue2);
                        case DT_INT:
                            return Arrays.equals((int    []) oValue1, (int    []) oValue2);
                        case DT_LONG:
                            return Arrays.equals((long   []) oValue1, (long   []) oValue2);
                        case DT_FLOAT:
                            return Arrays.equals((float  []) oValue1, (float  []) oValue2);
                        case DT_DOUBLE:
                            return Arrays.equals((double []) oValue1, (double []) oValue2);
                        default:
                            throw new IllegalStateException(CLASS + ".isValueEqual:  " +
                                "Unexpected Data Type " + dtElement);
                        }
                    }
                }
            catch (ClassCastException e)
                {
                // they are certainly not equal (as far as I can tell ;-)
                return false;
                }
            }
        else
            {
            // remaining "intrinsic" types, string, Component Definitions,
            // serializable objects
            return oValue1.equals(oValue2);
            }
        }


    // ----- Object methods -------------------------------------------------

    /**
    * Compare this Property to another Object for equality.
    *
    * @param obj  the other Object to compare to this
    *
    * @return true if this Property equals that Object
    */
    public boolean equals(Object obj)
        {
        if (obj instanceof Property)
            {
            Property that = (Property) obj;
            return this                  == that
                || this.m_nFlags         == that.m_nFlags
                && this.m_dt             == that.m_dt
                && this.m_fSizeDelta     == that.m_fSizeDelta
                && this.m_sName .equals(that.m_sName )
                && isValueEqual(m_dt, isSingle(), this.m_oValue    , that.m_oValue    )
                && super.equals(that);

            // PJM Removed the following because they are not persistent fields
            //&& this.m_fPrevSizeDelta == that.m_fPrevSizeDelta
            //&& isValueEqual(m_dt, isSingle(), this.m_oPrevValue, that.m_oPrevValue)
            }
        return false;
        }

    /**
    * Provide a human-readable description of the property.
    *
    * @return a string describing the property
    */
    public String toString()
        {
        StringBuffer sb = new StringBuffer();

        switch (getVisible())
            {
            case VIS_ADVANCED:
                sb.append("advanced ");
                break;
            case VIS_HIDDEN:
                sb.append("hidden ");
                break;
            case VIS_SYSTEM:
                sb.append("system ");
                break;
            }

        switch (getAccess())
            {
            case ACCESS_PUBLIC:
                sb.append("public ");
                break;
            case ACCESS_PROTECTED:
                sb.append("protected ");
                break;
            case ACCESS_PRIVATE:
                sb.append("private ");
                break;
            }

        if (isStatic())
            {
            sb.append("static ");
            }

        if (isFinal())
            {
            sb.append("final ");
            }

        sb.append(isPersistent() ? "persistent " : "transient ");

        switch (getDirection())
            {
            case DIR_IN:
                sb.append("in ");
                break;
            case DIR_OUT:
                sb.append("out ");
                break;
            case DIR_INOUT:
                sb.append("inout ");
                break;
            }

        if (!isSingle())
            {
            sb.append("indexed ");
            }

        sb.append(m_dt.toString())
          .append(' ')
          .append(m_sName);

        if (!isNoValue())
            {
            try
                {
                sb.append(" = ");
                if (isNullValue())
                    {
                    sb.append("null");
                    }
                else if (isSingle())
                    {
                    switch (getDataTypeEnum(m_dt))
                        {
                        case DT_BOOLEAN:
                            sb.append(m_oValue.toString());
                            break;

                        case DT_CHAR:
                            {
                            char ch = ((Character) m_oValue).charValue();
                            sb.append(toQuotedCharEscape(ch));
                            }
                            break;

                        case DT_BYTE:
                        case DT_SHORT:
                        case DT_INT:
                        case DT_LONG:
                        case DT_FLOAT:
                        case DT_DOUBLE:
                            sb.append(m_oValue.toString());
                            break;

                        case DT_BINARY:
                            {
                            byte[] ab = (byte[]) m_oValue;

                            boolean fTrunc = (ab.length > 16);
                            if (fTrunc)
                                {
                                byte[] abTemp = new byte[16];
                                System.arraycopy(ab, 0, abTemp, 0, abTemp.length);
                                ab = abTemp;
                                }

                            sb.append("0x")
                              .append(toHex(ab));

                            if (fTrunc)
                                {
                                sb.append("...");
                                }
                            }
                            break;

                        case DT_STRING:
                            {
                            String s = (String) m_oValue;
                            if (s.length() > 32)
                                {
                                s = s.substring(0, 32) + "...";
                                }
                            sb.append(toQuotedStringEscape(s));
                            }
                            break;

                        case DT_COMPLEX:
                            {
                            Component cd = (Component) m_oValue;
                            sb.append(cd.getQualifiedName())
                              .append(" {...}");
                            }
                            break;

                        case DT_SERIALIZABLE:
                        default:
                            sb.append("Serializable");
                            break;
                        }
                    }
                else
                    {
                    sb.append("{...}");
                    }
                }
            catch (ClassCastException cce)
                {
                sb.append("???");
                }
            }

        return sb.toString();
        }


    // ----- debug methods --------------------------------------------------

    /**
    * Provide the entire set of trait information in a printed format.
    *
    * @param out      PrintWriter object to dump the information to
    * @param sIndent  a string used to indent each line of dumped information
    */
    public void dump(PrintWriter out, String sIndent)
        {
        boolean fDerOrMod = (getMode() == MODIFICATION || getMode() == DERIVATION);

        // Name
        out.println(sIndent + "property " + (m_sName == null ? "" : m_sName));

        // dump generic trait attributes
        super.dump(out, sIndent);

        // Exists, Visible, Deprecated, Access, Static, Final, Persistent,
        // Indexed, and Direction (stored as bit flags)
        out.print  (sIndent + "flags=0x" + Integer.toHexString(m_nFlags));
        out.println(" (" + flagsDescription(m_nFlags, SPECABLE_FLAGS, fDerOrMod) + ')');

        // DataType
        out.println(sIndent + "type=" + (m_dt == null ? "" : m_dt.toString()));

        // Value
        byte nType = getDataTypeEnum();
        if (isSingle() || isNullValue() || isNoValue() || isNoDelta())
            {
            out.print(     sIndent + "value=");
            dumpValue(out, sIndent + "      ", nType, m_oValue);
            }
        else
            {
            dumpArrayValues(out, sIndent, nType, (Object[]) m_oValue);
            }

        // Previous Value
        Object o = m_oPrevValue;
        if (isSingle() || o == null || o == NO_VALUE || o == NO_DELTA)
            {
            out.print(     sIndent + "prev value=");
            dumpValue(out, sIndent + "           ", nType, o);
            }
        else
            {
            dumpArrayValues(out, sIndent, nType, (Object[]) o);
            }

        // delta size for arrays
        out.println(sIndent + "(delta size=" + m_fSizeDelta + ", prev delta size=" + m_fPrevSizeDelta + ')');
        }

    /**
    * Provide the entire set of trait information in a printed format.
    *
    * @param out      PrintWriter object to dump the information to
    * @param sIndent  a string used to indent each line of dumped information
    * @param nType
    * @param aoValue
    */
    protected void dumpArrayValues(PrintWriter out, String sIndent, byte nType, Object[] aoValue)
        {
        int cValues = aoValue.length;
        int cDigits = getMaxDecDigits(cValues);

        // print number of values:  "value [0..n]:"
        out.println(sIndent + "value [0.." + (cValues - 1) + "]:");

        // multi-line values need to be indented to line up with the
        // first line, which is "  [" + index + "]="
        String sIndentVal = sIndent + "     " + ("          ").substring(0, cDigits);

        // print each value
        for (int i = 0; i < cValues; ++i)
            {
            out.print(sIndent + "  [" + toDecString(i, cDigits) + "]=");
            dumpValue(out, sIndentVal, nType, aoValue[i]);
            }
        }

    /**
    * Provide the entire set of trait information in a printed format.
    *
    * @param out      PrintWriter object to dump the information to
    * @param sIndent  a string used to indent each line of dumped information
    */
    protected void dumpValue(PrintWriter out, String sIndent, byte nType, Object oValue)
        {
        if (oValue == null)
            {
            out.println("");
            }
        else if (oValue == NO_VALUE)
            {
            out.println("");
            }
        else if (oValue == NO_DELTA)
            {
            out.println("");
            }
        else
            {
            switch (nType)
                {
                case DT_BOOLEAN:
                    out.println(oValue.toString());
                    break;

                case DT_CHAR:
                    {
                    char ch = ((Character) oValue).charValue();
                    out.println(toQuotedCharEscape(ch));
                    }
                    break;

                case DT_BYTE:
                case DT_SHORT:
                case DT_INT:
                case DT_LONG:
                case DT_FLOAT:
                case DT_DOUBLE:
                    out.println(oValue.toString());
                    break;

                case DT_STRING:
                    out.println(toQuotedStringEscape((String) oValue));
                    break;

                case DT_BINARY:
                    {
                    byte[] ab = (byte[]) oValue;
                    out.println(indentString(toHexDump(ab, 16), sIndent, false));
                    }
                    break;

                case DT_COMPLEX:
                    {
                    Component cd = (Component) oValue;
                    cd.dump(out, sIndent, false);
                    }
                    break;

                case DT_SERIALIZABLE:
                    out.println("(java.io.Serializable) " + oValue.getClass().getName());
                    try
                        {
                        ByteArrayOutputStream streamRaw = new ByteArrayOutputStream();
                        ObjectOutputStream    streamObj = new ObjectOutputStream(streamRaw);
                        streamObj.writeObject((Serializable) oValue);
                        streamObj.flush();
                        streamObj.close();
                        byte[] ab = streamRaw.toByteArray();
                        out.println(indentString(toHexDump(ab, 16), sIndent));
                        }
                    catch (IOException e)
                        {
                        out.println(sIndent + "Unable to serialize!  (" + e.toString() + ')');
                        }
                    break;

                default:
                    out.println("");
                    break;
                }
            }
        }


    // ----- constants ------------------------------------------------------

    // ----- attributes

    /**
    * The Accessibility attribute.
    */
    public static final String ATTR_ACCESS      = "Access";

    /**
    * The Static attribute name.
    */
    public static final String ATTR_STATIC      = "Static";

    /**
    * The Final attribute name.
    */
    public static final String ATTR_FINAL       = "Final";

    /**
    * The Visible attribute name.
    */
    public static final String ATTR_VISIBLE     = "Visible";

    /**
    * The Deprecated attribute.
    */
    public static final String ATTR_DEPRECATED  = "Deprecated";

    /**
    * The Persistent attribute.
    */
    public static final String ATTR_PERSISTENT  = "Persistent";

    /**
    * The Direction attribute.
    */
    public static final String ATTR_DIRECTION   = "Direction";

    /**
    * The DataType attribute.
    */
    public static final String ATTR_DATATYPE    = "DataType";

    /**
    * The Indexed attribute.
    */
    public static final String ATTR_INDEXED     = "Indexed";

    /**
    * The Name attribute.
    */
    public static final String ATTR_NAME        = "Name";

    /**
    * The Value attribute.
    */
    public static final String ATTR_VALUE       = "Value";


    // ----- supported data types

    // These data types are supported for design, persistence, and
    // initialization.  Any valid Java data type can be used as the
    // type of a property, but only these types can have state set
    // up at design time.  For each data type, a corresponding int
    // value exists to allow for code optimizations (e.g. switch).

    /**
    * Java boolean.
    */
    public    static final DataType BOOLEAN     = DataType.BOOLEAN;
    protected static final byte  DT_BOOLEAN     = 0;

    /**
    * Java char.
    */
    public    static final DataType CHAR        = DataType.CHAR;
    protected static final byte  DT_CHAR        = 1;

    /**
    * Java byte.
    */
    public    static final DataType BYTE        = DataType.BYTE;
    protected static final byte  DT_BYTE        = 2;

    /**
    * Java short.
    */
    public    static final DataType SHORT       = DataType.SHORT;
    protected static final byte  DT_SHORT       = 3;

    /**
    * Java int.
    */
    public    static final DataType INT         = DataType.INT;
    protected static final byte  DT_INT         = 4;

    /**
    * Java long.
    */
    public    static final DataType LONG        = DataType.LONG;
    protected static final byte  DT_LONG        = 5;

    /**
    * Java float.
    */
    public    static final DataType FLOAT       = DataType.FLOAT;
    protected static final byte  DT_FLOAT       = 6;

    /**
    * Java double.
    */
    public    static final DataType DOUBLE      = DataType.DOUBLE;
    protected static final byte  DT_DOUBLE      = 7;

    /**
    * The binary Data Type.
    */
    public    static final DataType BINARY      = DataType.BINARY;
    protected static final byte  DT_BINARY      = 8;

    /**
    * The text (String) Data Type.
    */
    public    static final DataType STRING      = DataType.STRING;
    protected static final byte  DT_STRING      = 9;

    /**
    * The complex (Component Definition) Data Type.
    */
    public    static final DataType COMPLEX     = DataType.COMPLEX;
    protected static final byte  DT_COMPLEX     = 10;

    /**
    * The Serializable Data Type.
    */
    public    static final DataType SERIALIZABLE= DataType.SERIALIZABLE;
    protected static final byte  DT_SERIALIZABLE= 11;


    // ----- special values

    /**
    * The "No Value" value.
    */
    public    static final Object NO_VALUE      = new Object();

    /**
    * The "No Delta" value.  This value is produced as a result of extracting
    * a derivation or modification in which the value of the super or base
    * is identical to the value of this (resolved) component.
    */
    protected static final Object NO_DELTA      = new Object();

    // special values (including nulls) are stored as enumerated values
    protected static final byte E_NO_VALUE      = 0;
    protected static final byte E_NO_DELTA      = 1;
    protected static final byte E_NULL_REF      = 2;
    protected static final byte E_HAS_VALUE     = 3;

    /**
    * The default value for each supported Data Type, indexed by each type's
    * associated DT_ value.
    */
    protected static final Object[] DEFAULT_VALUES =
        {
        Boolean.FALSE,
        (byte) 0x00,
        '\0',
        (short) 0,
        0,
        0L,
        0.0F,
        0.0D,
        (byte[]) null,
        (String) null,
        (Component) null,
        (Serializable) null,
        };


    // ----- Property Accessors (for reserved behaviors)

    // reserved Behaviors are located by using the possible Behavior
    // signatures based on the Property type and name, including both
    // the "get" and "is" versions of the getters.  the rules for reserving
    // Behaviors are more general to avoid ambiguous Behaviors unrelated
    // to Properties but appearing by naming convention to be related.

    /**
    * Reserved Property Accessor:  Single setter.
    */
    public static final int RSVD_SET_SINGLE     = 0;

    /**
    * Reserved Property Accessor:  Array setter.
    */
    public static final int RSVD_SET_ARRAY      = 1;

    /**
    * Reserved Property Accessor:  Indexed setter.
    */
    public static final int RSVD_SET_INDEX      = 2;

    /**
    * Reserved Property Accessor:  Single or array getter.
    */
    public static final int RSVD_GET_EITHER     = 3;

    /**
    * Reserved Property Accessor:  Indexed getter.
    */
    public static final int RSVD_GET_INDEX      = 4;

    /**
    * Reserved Property Accessor:  Single or array boolean getter.
    */
    public static final int RSVD_IS_EITHER      = 5;

    /**
    * Reserved Property Accessor:  Indexed boolean getter.
    */
    public static final int RSVD_IS_INDEX       = 6;


    // ----- Property Accessors (for related behaviors)

    // related Behaviors are those Behaviors that actually exist to implement
    // the Property, and are exactly predictable based on the Property
    // declaration.  For example, an indexed inout boolean Property X would
    // have the following Behaviors:
    //
    //  [0]  null
    //  [1]  null
    //  [2]  boolean[] isX()
    //  [3]  void setX(boolean[])
    //  [4]  boolean isX(int)
    //  [5]  void setX(int, boolean)

    /**
    * Property Accessor:  Single getter (either "get" for non-boolean or
    * "is" for boolean).
    */
    public static final int PA_GET_SINGLE       = 0;
    public static final int PA_FIRST            = 0;

    /**
    * Property Accessor:  Single setter.
    */
    public static final int PA_SET_SINGLE       = 1;

    /**
    * Property Accessor:  Array  getter (either "get" for non-boolean or
    * "is" for boolean).
    */
    public static final int PA_GET_ARRAY        = 2;

    /**
    * Property Accessor:  Array setter.
    */
    public static final int PA_SET_ARRAY        = 3;

    /**
    * Property Accessor:  Indexed  getter (either "get" for non-boolean or
    * "is" for boolean).
    */
    public static final int PA_GET_INDEX        = 4;

    /**
    * Property Accessor:  Indexed setter.
    */
    public static final int PA_SET_INDEX        = 5;
    public static final int PA_LAST             = 5;


    // ----- misc

    /**
    * The name of this class.
    */
    private static final String CLASS = "Property";

    /**
    * The Property's descriptor string.
    */
    protected static final String DESCRIPTOR = CLASS;

    /**
    * The default flags.
    */
    protected static final int DEFAULT_FLAGS  = VIS_VISIBLE | ANTIQ_CURRENT;

    /**
    * The specifiable flags.
    */
    protected static final int SPECABLE_FLAGS = EXISTS_SPECIFIED |
                                                VIS_SPECIFIED    |
                                                ANTIQ_SPECIFIED  |
                                                ACCESS_SPECIFIED |
                                                SCOPE_SPECIFIED  |
                                                DERIVE_SPECIFIED |
                                                STG_SPECIFIED    |
                                                PROP_SPECIFIED   |
                                                DIR_SPECIFIED;

    /**
    * The flags that might affect class generation.
    */
    protected static final int CLASSGEN_FLAGS = VIS_MASK   |
                                                ANTIQ_MASK;

    /**
    * Certain attributes are only specifiable at the declaration level and
    * are copied to any derivation/modification level.
    */
    protected static final int DECL_ONLY_MASK = ACCESS_FULLMASK |
                                                SCOPE_FULLMASK  |
                                                DERIVE_FULLMASK |
                                                STG_FULLMASK    |
                                                PROP_FULLMASK   |
                                                DIR_FULLMASK;

    /**
    * Attributes which can be overriden at derivation/modification levels.
    */
    protected static final int OVERRIDE_FLAGS = VIS_SPECIFIED   |
                                                ANTIQ_SPECIFIED;

    /**
    * Empty array of DataTypes.
    */
    protected static final DataType[]   NO_PARAMS   = new DataType[0];

    /**
    * Empty array of Strings.
    */
    protected static final String[]     NO_NAMES    = new String[0];

    /**
    * Empty array of directions (integers).
    */
    protected static final int[]        NO_DIRS     = new int[0];

    /**
    * Empty array of Objects.
    */
    protected static final Object[]     NO_OBJECTS  = new Object[0];

    /**
    * Index parameter name.
    */
    protected static final String       INDEX_PARAM = "i";

    /**
    * Prefix for Property value parameter names.
    */
    protected static final String       VALUE_PARAM = "p";


    // ----- data members ---------------------------------------------------

    /**
    * The Property's name.  The name of the property uniquely identifies it
    * within the Component Definition.
    */
    private String m_sName;

    /**
    * The data type of the Property.
    */
    private DataType m_dt;

    /**
    * The attributes Exists, Visible, Deprecated, Access, Static, Final,
    * Persistent, Indexed, and Direction are stored as bit flags.
    */
    private int m_nFlags;

    /**
    * The value of the Property.  This member is polymorphic; the class
    * holding the Property value is determined by the Property data type.
    */
    private Object m_oValue;

    /**
    * The default value of the Property.  For the Property declaration level,
    * this is always "no value".  For any derived or modified Property, this
    * is the value from the super or base level.
    */
    private Object m_oPrevValue;

    /**
    * If the Property is indexed and the value of the Property is not equal
    * to the value of the base Property in that the number of indexes differs
    * then the resulting modification/derivation has a "size delta".  For
    * purposes of resolving a modification/derivation, this differentiates
    * between a size difference caused by explicit add/remove at this level
    * vs. a later add/remove at a base level.  (Without a "size delta",
    * elements which are added/removed at a base level are reflected in this
    * level; with a "size delta", such base changes are ignored.)
    */
    private boolean m_fSizeDelta;

    /**
    * The value of the most recently applied-to base's m_fSizeDelta member.
    * Used during resolve() only.
    */
    private transient boolean m_fPrevSizeDelta;

    protected void setBooleanGet(boolean fGet)
        {
        m_fBooleanGet = fGet;
        }
    protected boolean isBooleanGet()
        {
        return m_fBooleanGet;
        }
    private transient boolean m_fBooleanGet;
    }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy