com.tangosol.dev.component.Property Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of coherence Show documentation
Show all versions of coherence Show documentation
Oracle Coherence Community Edition
/*
* 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;
}