0001: /*
0002: * This file is part of the Echo Web Application Framework (hereinafter "Echo").
0003: * Copyright (C) 2002-2005 NextApp, Inc.
0004: *
0005: * Version: MPL 1.1/GPL 2.0/LGPL 2.1
0006: *
0007: * The contents of this file are subject to the Mozilla Public License Version
0008: * 1.1 (the "License"); you may not use this file except in compliance with
0009: * the License. You may obtain a copy of the License at
0010: * http://www.mozilla.org/MPL/
0011: *
0012: * Software distributed under the License is distributed on an "AS IS" basis,
0013: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0014: * for the specific language governing rights and limitations under the
0015: * License.
0016: *
0017: * Alternatively, the contents of this file may be used under the terms of
0018: * either the GNU General Public License Version 2 or later (the "GPL"), or
0019: * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
0020: * in which case the provisions of the GPL or the LGPL are applicable instead
0021: * of those above. If you wish to allow use of your version of this file only
0022: * under the terms of either the GPL or the LGPL, and not to allow others to
0023: * use your version of this file under the terms of the MPL, indicate your
0024: * decision by deleting the provisions above and replace them with the notice
0025: * and other provisions required by the GPL or the LGPL. If you do not delete
0026: * the provisions above, a recipient may use your version of this file under
0027: * the terms of any one of the MPL, the GPL or the LGPL.
0028: */
0029:
0030: package nextapp.echo2.app;
0031:
0032: import java.beans.PropertyChangeListener;
0033: import java.beans.PropertyChangeSupport;
0034: import java.io.Serializable;
0035: import java.util.ArrayList;
0036: import java.util.Iterator;
0037: import java.util.List;
0038: import java.util.Locale;
0039:
0040: import nextapp.echo2.app.event.EventListenerList;
0041:
0042: /**
0043: * A representation of an Echo component. This is an abstract base class from
0044: * which all Echo components are derived.
0045: * <p>
0046: * A hierarchy of <code>Component</code> objects is used to represent the
0047: * state of an application's user interface. A <code>Component</code> may have
0048: * a single parent <code>Component</code> and may contain zero or more child
0049: * <code>Component</code>s. Certain <code>Component</code>s may limit the
0050: * number or type(s) of children which may be added to them, and may even
0051: * establish requirements for what type(s) of parent <code>Component</code>s
0052: * they may be added to. In the event that an application attempts to add a
0053: * child <code>Component</code> to a parent <code>Component</code> in spite
0054: * of these requirements, an <code>IllegalChildException</code> is thrown.
0055: *
0056: * <h3>Properties and Styles</h3>
0057: * <p>
0058: * The state of a single <code>Component</code> is represented by its
0059: * properties. Properties can be categorized into two types: "style" and
0060: * "non-style". Style properties are generally used to represent the
0061: * "look-and-feel" of a Component--information such as colors, fonts, location,
0062: * and borders. "Non-style" properties are generally used to represent
0063: * non-stylistic information such as data models, selection models, and locale.
0064: * <p>
0065: * "Style Properties" have a special definition because they may be stored in
0066: * <code>Style</code> or <code>StyleSheet</code> objects instead of as
0067: * properties of a specific <code>Component</code> instance. Property values
0068: * contained in a relevant <code>Style</code> or <code>StyleSheet</code>
0069: * will be used for rendering when the property values are not specified by a
0070: * <code>Component</code> itself. Style properties are identified by the
0071: * presence of a public static constant name in a <code>Component</code>
0072: * implementation with the prefix <code>PROPERTY_</code>. In the base
0073: * <code>Component</code> class itself there are several examples of style
0074: * properties, such as <code>PROPERTY_BACKGROUND</code>,<code>PROPERTY_FONT</code>
0075: * and <code>PROPERTY_LAYOUT_DATA</code>. The rendering application container
0076: * will use the <code>Component.getRenderProperty()</code> and
0077: * <code>Component.getRenderIndexedProperty()</code> to retrieve the values of
0078: * stylistic properties, in order that their values might be obtained from
0079: * the <code>Component</code>'s shared <code>Style</code> or the
0080: * <code>ApplicationInstance</code>'s <code>StyleSheet</code> in the event
0081: * they are not directly set in the <code>Component</code>.
0082: * <p>
0083: * A <code>Component</code> implementation should not store the values of
0084: * style properties as instance variables. Rather, the values of style
0085: * properties should be stored in the local <code>Style</code> instance, by
0086: * way of the <code>setProperty()</code> method. The
0087: * <code>getProperty()</code> method may be used to obtain the value of such
0088: * properties. Only style properties should be stored using these methods;
0089: * properties such as models should never be stored using the
0090: * <code>getProperty()</code>/<code>setProperty()</code> interface.
0091: *
0092: * <h3>Events</h3>
0093: * <p>
0094: * Many <code>Component</code>s will provide the capability to register
0095: * <code>EventListener</code>s to notify interested parties when various
0096: * state changes occur. The base <code>Component</code> class provides an
0097: * <code>EventListenerList</code> as a convenient and memory efficient means
0098: * of storing such listeners. The internal <code>EventListenerList</code> may
0099: * be obtained using the <code>getEventListenerList()</code> method. The
0100: * <code>EventListenerList</code> is lazy-created and will only be
0101: * instantiated on the first invocation of the
0102: * <code>getEventListenerList()</code> method. If the intent is only to
0103: * inquire about the state of event listeners without necessarily forcing
0104: * instantiation of an <code>EventListenerList</code>, the
0105: * <code>hasEventListenerList()</code> should be queried prior to invoking
0106: * <code>getEventListenerList()</code>.
0107: */
0108: public abstract class Component implements RenderIdSupport,
0109: Serializable {
0110:
0111: /**
0112: * <code>ArrayList</code> capacity for child storage.
0113: */
0114: private static final int CHILD_LIST_CAPACITY = 3;
0115:
0116: /**
0117: * Empty array returned by <code>getComponents()</code> when a
0118: * <code>Component</code> has no children.
0119: */
0120: private static final Component[] EMPTY_COMPONENT_ARRAY = new Component[0];
0121:
0122: /**
0123: * Flag indicating the <code>Component</code> is enabled.
0124: */
0125: private static final int FLAG_ENABLED = 0x1;
0126:
0127: /**
0128: * Flag indicating the <code>Component</code> is visible.
0129: */
0130: private static final int FLAG_VISIBLE = 0x2;
0131:
0132: /**
0133: * Flag indicating the <code>Component</code> will participate in the
0134: * focus traversal order.
0135: */
0136: private static final int FLAG_FOCUS_TRAVERSAL_PARTICIPANT = 0x4;
0137:
0138: /**
0139: * Flag indicating that the <code>Component</code> is currently undergoing
0140: * registration to an <code>ApplicationInstance</code>.
0141: */
0142: private static final int FLAG_REGISTERING = 0x8;
0143:
0144: private static final int FLAG_INIT_IN_PROGRESS = 0x10;
0145:
0146: private static final int FLAG_DISPOSE_IN_PROGRESS = 0x20;
0147:
0148: private static final int FLAG_INITIALIZED = 0x40;
0149:
0150: /**
0151: * Flag mask of bits used for storage of focus traversal index.
0152: */
0153: private static final int FLAGS_FOCUS_TRAVERSAL_INDEX = 0x7fff0000;
0154:
0155: public static final String CHILDREN_CHANGED_PROPERTY = "children";
0156: public static final String ENABLED_CHANGED_PROPERTY = "enabled";
0157: public static final String FOCUS_TRAVERSAL_INDEX_CHANGED_PROPERTY = "focusTraversalIndex";
0158: public static final String FOCUS_TRAVERSAL_PARTICIPANT_CHANGED_PROPERTY = "focusTraversalParticipant";
0159: public static final String LAYOUT_DIRECTION_CHANGED_PROPERTY = "layoutDirection";
0160: public static final String LOCALE_CHANGED_PROPERTY = "locale";
0161:
0162: public static final String PROPERTY_BACKGROUND = "background";
0163: public static final String PROPERTY_FONT = "font";
0164: public static final String PROPERTY_FOREGROUND = "foreground";
0165: public static final String PROPERTY_LAYOUT_DATA = "layoutData";
0166: public static final String STYLE_CHANGED_PROPERTY = "style";
0167: public static final String STYLE_NAME_CHANGED_PROPERTY = "styleName";
0168: public static final String VISIBLE_CHANGED_PROPERTY = "visible";
0169:
0170: //TODO. Doc/move to util.
0171: private static final boolean isLetter(char ch) {
0172: return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
0173: }
0174:
0175: //TODO. Doc/move to util.
0176: private static final boolean isLetterOrDigit(char ch) {
0177: return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')
0178: || (ch >= '0' && ch <= '9');
0179: }
0180:
0181: /** The <code>ApplicationInstance</code> to which the component is registered. */
0182: private ApplicationInstance applicationInstance;
0183:
0184: /**
0185: * An ordered collection of references to child components.
0186: * This object is lazily instantiated.
0187: */
0188: private List children;
0189:
0190: /**
0191: * Boolean flags for this component, including enabled state, visibility,
0192: * focus traversal participation, and focus traversal index.
0193: * Multiple booleans are wrapped in a single integer
0194: * to save memory, since many <code>Component</code>instances will be
0195: * created.
0196: */
0197: private int flags;
0198:
0199: /**
0200: * A user-defined identifier for this component.
0201: * This identifier is not related in any way to <code>renderId</code>.
0202: */
0203: private String id;
0204:
0205: /**
0206: * The layout direction of the component.
0207: * This property is generally unset, as layout direction information is
0208: * normally inherited from the <code>ApplicationInstance</code> or from
0209: * an ancestor <code>Component</code> in the hierarchy.
0210: */
0211: private LayoutDirection layoutDirection;
0212:
0213: /** Listener storage. */
0214: private EventListenerList listenerList;
0215:
0216: /**
0217: * The locale of the component.
0218: * This property is generally unset, as locale information is normally
0219: * inherited from the <code>ApplicationInstance</code> or from an ancestor
0220: * <code>Component</code> in the hierarchy.
0221: */
0222: private Locale locale;
0223:
0224: /** Local style data storage for properties directly set on component itself. */
0225: private MutableStyle localStyle;
0226:
0227: /** The parent component. */
0228: private Component parent;
0229:
0230: /**
0231: * The property change event dispatcher.
0232: * This object is lazily instantiated.
0233: */
0234: private PropertyChangeSupport propertyChangeSupport;
0235:
0236: /**
0237: * A application-wide unique identifier for this component.
0238: * This identifier is not related in any way to <code>id</code>.
0239: */
0240: private String renderId;
0241:
0242: /** Shared style. */
0243: private Style sharedStyle;
0244:
0245: /** Name of style to use from application style sheet */
0246: private String styleName;
0247:
0248: /**
0249: * Creates a new <code>Component</code>.
0250: */
0251: public Component() {
0252: super ();
0253: flags = FLAG_ENABLED | FLAG_VISIBLE
0254: | FLAG_FOCUS_TRAVERSAL_PARTICIPANT;
0255: localStyle = new MutableStyle();
0256: }
0257:
0258: /**
0259: * Adds the specified <code>Component</code> as a child of this
0260: * <code>Component</code>. The child will be added at the greatest
0261: * index.
0262: *
0263: * @param c the child <code>Component</code> to add
0264: */
0265: public void add(Component c) {
0266: add(c, -1);
0267: }
0268:
0269: /**
0270: * Adds the specified <code>Component</code> as the <code>n</code>th
0271: * child of this component.
0272: * All component-add operations use this method to add components.
0273: * <code>Component</code>s that require notification of all child additions
0274: * should override this method (making sure to call the superclass'
0275: * implementation).
0276: *
0277: * @param c the child component to add
0278: * @param n the index at which to add the child component, or -1 to add the
0279: * component at the end
0280: * @throws IllegalChildException if the child is not allowed to be added
0281: * to this component, because it is either not valid for the
0282: * component's state or is of an invalid type
0283: */
0284: public void add(Component c, int n) throws IllegalChildException {
0285:
0286: // Ensure child is acceptable to this component.
0287: if (!isValidChild(c)) {
0288: throw new IllegalChildException(this , c);
0289: }
0290:
0291: // Ensure child component finds this component acceptable as a parent.
0292: if (!c.isValidParent(this )) {
0293: throw new IllegalChildException(this , c);
0294: }
0295:
0296: // Remove child from it's current parent if required.
0297: if (c.parent != null) {
0298: c.parent.remove(c);
0299: }
0300:
0301: // Lazy-create child collection if necessary.
0302: if (children == null) {
0303: children = new ArrayList(CHILD_LIST_CAPACITY);
0304: }
0305:
0306: // Connect child to parent.
0307: c.parent = this ;
0308: if (n == -1) {
0309: children.add(c);
0310: } else {
0311: children.add(n, c);
0312: }
0313:
0314: // Flag child as registered.
0315: if (applicationInstance != null) {
0316: c.register(applicationInstance);
0317: }
0318:
0319: // Notify PropertyChangeListeners of change.
0320: firePropertyChange(CHILDREN_CHANGED_PROPERTY, null, c);
0321:
0322: // Initialize component.
0323: c.doInit();
0324: }
0325:
0326: /**
0327: * Adds a property change listener to this component.
0328: *
0329: * @param l the listener to add
0330: */
0331: public void addPropertyChangeListener(PropertyChangeListener l) {
0332: if (propertyChangeSupport == null) {
0333: propertyChangeSupport = new PropertyChangeSupport(this );
0334: }
0335: propertyChangeSupport.addPropertyChangeListener(l);
0336: }
0337:
0338: /**
0339: * Internal method to set the render identifier of the<code>Component</code>.
0340: * This method is invoked by the <code>ApplicationInstance</code>
0341: * when the component is registered or unregistered, or by manual
0342: * invocation of <code>setRenderId()</code>. This method performs no
0343: * error checking.
0344: *
0345: * @param renderId the new identifier
0346: * @see #getRenderId()
0347: * @see #setRenderId()
0348: */
0349: void assignRenderId(String renderId) {
0350: this .renderId = renderId;
0351: }
0352:
0353: /**
0354: * Life-cycle method invoked when the <code>Component</code> is removed
0355: * from a registered hierarchy. Implementations should always invoke
0356: * <code>super.dispose()</code>.
0357: * Modifications to the component hierarchy are not allowed within this
0358: * method.
0359: */
0360: public void dispose() {
0361: }
0362:
0363: /**
0364: * Recursively executes the <code>dispose()</code> life-cycle methods of
0365: * this <code>Component</code> and its descendants.
0366: */
0367: void doDispose() {
0368: if (applicationInstance == null) {
0369: return;
0370: }
0371: if ((flags & (FLAG_INIT_IN_PROGRESS | FLAG_DISPOSE_IN_PROGRESS)) != 0) {
0372: throw new IllegalStateException(
0373: "Attempt to dispose component when initialize or dispose operation already in progress.");
0374: }
0375: flags |= FLAG_DISPOSE_IN_PROGRESS;
0376: try {
0377: if (children != null) {
0378: Iterator it = children.iterator();
0379: while (it.hasNext()) {
0380: ((Component) it.next()).doDispose();
0381: }
0382: }
0383: if ((flags & FLAG_INITIALIZED) == 0) {
0384: // Component already disposed.
0385: return;
0386: }
0387: dispose();
0388: flags &= ~FLAG_INITIALIZED;
0389: } finally {
0390: flags &= ~FLAG_DISPOSE_IN_PROGRESS;
0391: }
0392: }
0393:
0394: /**
0395: * Recursively executes the <code>init()</code> life-cycle methods of
0396: * this <code>Component</code> and its descendants.
0397: */
0398: void doInit() {
0399: if (applicationInstance == null) {
0400: return;
0401: }
0402: if ((flags & FLAG_INITIALIZED) != 0) {
0403: // Component already initialized.
0404: return;
0405: }
0406:
0407: if ((flags & (FLAG_INIT_IN_PROGRESS | FLAG_DISPOSE_IN_PROGRESS)) != 0) {
0408: throw new IllegalStateException(
0409: "Attempt to initialize component when initialize or dispose operation already in progress.");
0410: }
0411: flags |= FLAG_INIT_IN_PROGRESS;
0412: try {
0413: init();
0414: flags |= FLAG_INITIALIZED;
0415: if (children != null) {
0416: Iterator it = children.iterator();
0417: while (it.hasNext()) {
0418: ((Component) it.next()).doInit();
0419: }
0420: }
0421: } finally {
0422: flags &= ~FLAG_INIT_IN_PROGRESS;
0423: }
0424: }
0425:
0426: /**
0427: * Reports a bound property change to <code>PropertyChangeListener</code>s
0428: * and to the <code>ApplicationInstance</code>'s update management system.
0429: *
0430: * @param propertyName the name of the changed property
0431: * @param oldValue the previous value of the property
0432: * @param newValue the present value of the property
0433: */
0434: protected void firePropertyChange(String propertyName,
0435: Object oldValue, Object newValue) {
0436: // Report to PropertyChangeListeners.
0437: if (propertyChangeSupport != null) {
0438: propertyChangeSupport.firePropertyChange(propertyName,
0439: oldValue, newValue);
0440: }
0441:
0442: // Report to ApplicationInstance.
0443: // The ApplicationInstance is notified directly in order to reduce
0444: // per-Component-instance memory requirements, i.e., it enables the
0445: // PropertyChangeSupport object to only be instantiated on Components
0446: // that have ProperyChangeListeners registered by a third party.
0447: if (applicationInstance != null) {
0448: applicationInstance.notifyComponentPropertyChange(this ,
0449: propertyName, oldValue, newValue);
0450: }
0451: }
0452:
0453: /**
0454: * Returns the <code>ApplicationInstance</code> to which this
0455: * <code>Component</code> is registered, or null if it is not currently
0456: * registered.
0457: *
0458: * @return the application instance
0459: */
0460: public ApplicationInstance getApplicationInstance() {
0461: return applicationInstance;
0462: }
0463:
0464: /**
0465: * Returns the default/base background color of the <code>Component</code>.
0466: * This property may not be relevant to certain components, though
0467: * even in such cases may be useful for setting a default for
0468: * children.
0469: *
0470: * @return the background color
0471: */
0472: public Color getBackground() {
0473: return (Color) localStyle.getProperty(PROPERTY_BACKGROUND);
0474: }
0475:
0476: /**
0477: * Returns the <code>n</code>th immediate child component.
0478: *
0479: * @param n the index of the <code>Component</code> to retrieve
0480: * @return the <code>Component</code> at index <code>n</code>
0481: * @throws IndexOutOfBoundsException when the index is invalid
0482: */
0483: public final Component getComponent(int n) {
0484: if (children == null) {
0485: throw new IndexOutOfBoundsException();
0486: }
0487:
0488: return (Component) children.get(n);
0489: }
0490:
0491: /**
0492: * Recursively searches for the component with the specified id
0493: * by querying this component and its descendants.
0494: * The id value is that retrieved and set via the <code>getId()</code>
0495: * and <code>setId()</code> methods. This method is in no way
0496: * related to <code>renderId</code>s.
0497: *
0498: * @param id the user-defined id of the component to be retrieved
0499: * @return the component with the specified id, if it either is this
0500: * component or is a descendant of it, or null otherwise
0501: */
0502: public final Component getComponent(String id) {
0503: if (id.equals(this .id)) {
0504: return this ;
0505: }
0506: if (children == null) {
0507: return null;
0508: }
0509: Iterator it = children.iterator();
0510: while (it.hasNext()) {
0511: Component testComponent = (Component) it.next();
0512: Component targetComponent = testComponent.getComponent(id);
0513: if (targetComponent != null) {
0514: return targetComponent;
0515: }
0516: }
0517: return null;
0518: }
0519:
0520: /**
0521: * Returns the number of immediate child <code>Component</code>s.
0522: *
0523: * @return the number of immediate child <code>Component</code>s
0524: */
0525: public final int getComponentCount() {
0526: if (children == null) {
0527: return 0;
0528: } else {
0529: return children.size();
0530: }
0531: }
0532:
0533: /**
0534: * Returns an array of all immediate child <code>Component</code>s.
0535: *
0536: * @return an array of all immediate child <code>Component</code>s
0537: */
0538: public final Component[] getComponents() {
0539: if (children == null) {
0540: return EMPTY_COMPONENT_ARRAY;
0541: } else {
0542: return (Component[]) children
0543: .toArray(new Component[children.size()]);
0544: }
0545: }
0546:
0547: /**
0548: * Returns the local <code>EventListenerList</code>.
0549: * The listener list is lazily created; invoking this method will
0550: * create the <code>EventListenerList</code> if required.
0551: *
0552: * @return the listener list
0553: */
0554: protected EventListenerList getEventListenerList() {
0555: if (listenerList == null) {
0556: listenerList = new EventListenerList();
0557: }
0558: return listenerList;
0559: }
0560:
0561: /**
0562: * Returns the focus traversal (tab) index of the component.
0563: * Components with numerically lower indices will be focused before
0564: * components with numerically higher indices. The value 0 has special
0565: * meaning, in that components with a value of 0 will be focused last.
0566: * The default value is 0.
0567: *
0568: * @return the focus traversal index, a value between 0 and 32767
0569: */
0570: public final int getFocusTraversalIndex() {
0571: return (flags & FLAGS_FOCUS_TRAVERSAL_INDEX) >> 16;
0572: }
0573:
0574: /**
0575: * Returns the default/base font of the component.
0576: * This property may not be relevant to certain components, though
0577: * even in such cases may be useful for setting a default for
0578: * children.
0579: *
0580: * @return the font
0581: */
0582: public Font getFont() {
0583: return (Font) localStyle.getProperty(PROPERTY_FONT);
0584: }
0585:
0586: /**
0587: * Returns the default/base foreground color of the <code>Component</code>.
0588: * This property may not be relevant to certain components, though
0589: * even in such cases may be useful for setting a default for
0590: * children.
0591: *
0592: * @return the foreground color
0593: */
0594: public Color getForeground() {
0595: return (Color) localStyle.getProperty(PROPERTY_FOREGROUND);
0596: }
0597:
0598: /**
0599: * Returns the user-defined identifier of the <code>Component</code>.
0600: * Note that the user defined identifier has no relation to the
0601: * <code>renderId</code>.
0602: *
0603: * @return the user-defined identifier
0604: */
0605: public String getId() {
0606: return id;
0607: }
0608:
0609: /**
0610: * Returns the value of the specified indexed property.
0611: * This method is generally used only internally by a
0612: * <code>Component</code>, however there are exceptions.
0613: * The more specific <code>getXXX()</code> methods to retrieve
0614: * property values from a <code>Component</code> whenever
0615: * possible.
0616: * See the class-level documentation for a more detailed
0617: * explanation of the use of this method.
0618: *
0619: * @param propertyName the property name
0620: * @param propertyIndex the property index
0621: * @return the property value
0622: */
0623: public final Object getIndexedProperty(String propertyName,
0624: int propertyIndex) {
0625: return localStyle.getIndexedProperty(propertyName,
0626: propertyIndex);
0627: }
0628:
0629: /**
0630: * Returns the <code>LayoutData</code> object used to describe how this
0631: * <code>Component</code> should be laid out within its parent container.
0632: *
0633: * @return the layout data, or null if unset
0634: * @see LayoutData
0635: */
0636: public LayoutData getLayoutData() {
0637: return (LayoutData) localStyle
0638: .getProperty(PROPERTY_LAYOUT_DATA);
0639: }
0640:
0641: /**
0642: * Returns the specific layout direction setting of this component, if any.
0643: * This method will return null unless a <code>LayoutDirection</code> is
0644: * specifically set on <strong>this</strong> <code>Component</code>.
0645: *
0646: * @return the layout direction property of <strong>this</strong>
0647: * <code>Component</code>
0648: * @see #getRenderLayoutDirection()
0649: */
0650: public LayoutDirection getLayoutDirection() {
0651: return layoutDirection;
0652: }
0653:
0654: /**
0655: * Returns the specific locale setting of this component, if any.
0656: * This method will return null unless a <code>Locale</code> is
0657: * specifically set on <strong>this</strong> <code>Component</code>.
0658: *
0659: * @return the locale property of <strong>this</strong>
0660: * <code>Component</code>
0661: * @see #getRenderLocale()
0662: */
0663: public Locale getLocale() {
0664: return locale;
0665: }
0666:
0667: /**
0668: * Returns the parent component.
0669: *
0670: * @return the parent component, or null if this component has no parent
0671: */
0672: public final Component getParent() {
0673: return parent;
0674: }
0675:
0676: /**
0677: * Returns the value of the specified property.
0678: * This method is generally used only internally by a
0679: * <code>Component</code>, however there are exceptions.
0680: * The more specific <code>getXXX()</code> methods to retrieve
0681: * property values from a <code>Component</code> whenever
0682: * possible.
0683: * See the class-level documentation for a more detailed
0684: * explanation of the use of this method.
0685: *
0686: * @param propertyName the property name
0687: * @return the property value
0688: */
0689: public final Object getProperty(String propertyName) {
0690: return localStyle.getProperty(propertyName);
0691: }
0692:
0693: /**
0694: * Returns the render id of this component.
0695: * This id is only guaranteed to be unique within
0696: * the <code>ApplicationInstance</code> to which this component is
0697: * registered. This method returns null in the event that the
0698: * component is not registered to an <code>ApplicationInstance</code>.
0699: *
0700: * @return the <code>ApplicationInstance</code>-wide unique id of this
0701: * component
0702: * @see nextapp.echo2.app.RenderIdSupport#getRenderId()
0703: */
0704: public String getRenderId() {
0705: return renderId;
0706: }
0707:
0708: /**
0709: * Determines the "rendered state" of an indexed property.
0710: * The rendered state is determined by first determining if the given
0711: * property is locally set on this <code>Component</code>, and returning
0712: * it in that case. If the property state is not set locally, the
0713: * shared <code>Style</code> assigned to this component will be queried
0714: * for the property value. If the property state is not set in the
0715: * shared <code>Style</code>, the <code>StyleSheet</code> of the
0716: * <code>ApplicationInstance</code> to which this <code>Component</code>
0717: * is registered will be queried for the property value.
0718: * In the event the property is not set in any of these resources,
0719: * null is returned.
0720: * <p>
0721: * The application container will invoke this method
0722: * rather than individual property getter methods to determine the state
0723: * of properties when rendering.
0724: *
0725: * @param propertyName the name of the property
0726: * @return the rendered property value
0727: */
0728: public final Object getRenderIndexedProperty(String propertyName,
0729: int propertyIndex) {
0730: return getRenderIndexedProperty(propertyName, propertyIndex,
0731: null);
0732: }
0733:
0734: /**
0735: * Determines the "rendered state" of an indexed property.
0736: * The rendered state is determined by first determining if the given
0737: * property is locally set on this <code>Component</code>, and returning
0738: * it in that case. If the property state is not set locally, the
0739: * shared <code>Style</code> assigned to this component will be queried
0740: * for the property value. If the property state is not set in the
0741: * shared <code>Style</code>, the <code>StyleSheet</code> of the
0742: * <code>ApplicationInstance</code> to which this <code>Component</code>
0743: * is registered will be queried for the property value.
0744: * In the event the property is not set in any of these resources,
0745: * <code>defaultValue</code> is returned.
0746: *
0747: * @param propertyName the name of the property
0748: * @param defaultValue the value to be returned if the property is not set
0749: * @return the property state
0750: */
0751: public final Object getRenderIndexedProperty(String propertyName,
0752: int propertyIndex, Object defaultValue) {
0753: if (localStyle
0754: .isIndexedPropertySet(propertyName, propertyIndex)) {
0755: // Return local style value.
0756: return localStyle.getIndexedProperty(propertyName,
0757: propertyIndex);
0758: } else if (sharedStyle != null
0759: && sharedStyle.isIndexedPropertySet(propertyName,
0760: propertyIndex)) {
0761: // Return style value specified in shared style.
0762: return sharedStyle.getIndexedProperty(propertyName,
0763: propertyIndex);
0764: } else {
0765: if (applicationInstance != null) {
0766: Style applicationStyle = applicationInstance.getStyle(
0767: getClass(), styleName);
0768: if (applicationStyle != null
0769: && applicationStyle.isIndexedPropertySet(
0770: propertyName, propertyIndex)) {
0771: // Return style value specified in application.
0772: return applicationStyle.getIndexedProperty(
0773: propertyName, propertyIndex);
0774: }
0775: }
0776: return defaultValue;
0777: }
0778: }
0779:
0780: /**
0781: * Returns the rendered <code>LayoutDirection</code> of the
0782: * <code>Component</code>.
0783: *
0784: * @return the layout direction of this component
0785: */
0786: public final LayoutDirection getRenderLayoutDirection() {
0787: if (layoutDirection == null) {
0788: if (locale == null) {
0789: if (parent == null) {
0790: if (applicationInstance == null) {
0791: return null;
0792: } else {
0793: return applicationInstance.getLayoutDirection();
0794: }
0795: } else {
0796: return parent.getRenderLayoutDirection();
0797: }
0798: } else {
0799: return LayoutDirection.forLocale(locale);
0800: }
0801: } else {
0802: return layoutDirection;
0803: }
0804: }
0805:
0806: /**
0807: * Returns the rendered <code>Locale</code> of the <code>Component</code>.
0808: * If this <code>Component</code> does not itself specify a locale, its
0809: * ancestors will be queried recursively until a <code>Component</code>
0810: * providing a <code>Locale</code> is found. If no ancestors have
0811: * <code>Locale</code>s set, the <code>ApplicationInstance</code>'s
0812: * locale will be returned. In the event that no locale information is
0813: * available from the ancestral hierarchy of <code>Component</code>s and
0814: * no <code>ApplicationInstance</code> is registered, null is returned.
0815: *
0816: * @return the locale for this component
0817: */
0818: public final Locale getRenderLocale() {
0819: if (locale == null) {
0820: if (parent == null) {
0821: if (applicationInstance == null) {
0822: return null;
0823: } else {
0824: return applicationInstance.getLocale();
0825: }
0826: } else {
0827: return parent.getRenderLocale();
0828: }
0829: } else {
0830: return locale;
0831: }
0832: }
0833:
0834: /**
0835: * Determines the "rendered state" of a property.
0836: * The rendered state is determined by first determining if the given
0837: * property is locally set on this <code>Component</code>, and returning
0838: * it in that case. If the property state is not set locally, the
0839: * shared <code>Style</code> assigned to this component will be queried
0840: * for the property value. If the property state is not set in the
0841: * shared <code>Style</code>, the <code>StyleSheet</code> of the
0842: * <code>ApplicationInstance</code> to which this <code>Component</code>
0843: * is registered will be queried for the property value.
0844: * In the event the property is not set in any of these resources,
0845: * null is returned.
0846: * <p>
0847: * The application container will invoke this method
0848: * rather than individual property getter methods to determine the state
0849: * of properties when rendering.
0850: *
0851: * @param propertyName the name of the property
0852: * @return the rendered property value
0853: */
0854: public final Object getRenderProperty(String propertyName) {
0855: return getRenderProperty(propertyName, null);
0856: }
0857:
0858: /**
0859: * Determines the "rendered state" of a property.
0860: * The rendered state is determined by first determining if the given
0861: * property is locally set on this <code>Component</code>, and returning
0862: * it in that case. If the property state is not set locally, the
0863: * shared <code>Style</code> assigned to this component will be queried
0864: * for the property value. If the property state is not set in the
0865: * shared <code>Style</code>, the <code>StyleSheet</code> of the
0866: * <code>ApplicationInstance</code> to which this <code>Component</code>
0867: * is registered will be queried for the property value.
0868: * In the event the property is not set in any of these resources,
0869: * <code>defaultValue</code> is returned.
0870: *
0871: * @param propertyName the name of the property
0872: * @param defaultValue the value to be returned if the property is not set
0873: * @return the property state
0874: */
0875: public final Object getRenderProperty(String propertyName,
0876: Object defaultValue) {
0877: Object propertyValue = localStyle.getProperty(propertyName);
0878: if (propertyValue != null) {
0879: return propertyValue;
0880: }
0881: if (sharedStyle != null) {
0882: propertyValue = sharedStyle.getProperty(propertyName);
0883: if (propertyValue != null) {
0884: return propertyValue;
0885: }
0886: }
0887: if (applicationInstance != null) {
0888: Style applicationStyle = applicationInstance.getStyle(
0889: getClass(), styleName);
0890: if (applicationStyle != null) {
0891: // Return style value specified in application.
0892: propertyValue = applicationStyle
0893: .getProperty(propertyName);
0894: if (propertyValue != null) {
0895: return propertyValue;
0896: }
0897: }
0898: }
0899: return defaultValue;
0900: }
0901:
0902: /**
0903: * Returns the shared <code>Style</code> object assigned to this
0904: * <code>Component</code>.
0905: * As its name implies, the <strong>shared</strong> <code>Style</code>
0906: * may be shared amongst multiple <code>Component</code>s.
0907: * Style properties will be rendered from the specified <code>Style</code>
0908: * when they are not specified locally in the <code>Component</code>
0909: * itself.
0910: *
0911: * @return the shared <code>Style</code>
0912: */
0913: public final Style getStyle() {
0914: return sharedStyle;
0915: }
0916:
0917: /**
0918: * Returns the name of the <code>Style</code> in the
0919: * <code>ApplicationInstance</code>'s<code>StyleSheet</code> from
0920: * which the renderer will retrieve properties. The renderer will only query
0921: * the <code>StyleSheet</code> when properties are not specified directly
0922: * by the <code>Component</code> or by the <code>Component</code>'s
0923: * shared <code>Style</code>.
0924: *
0925: * @return the style name
0926: */
0927: public final String getStyleName() {
0928: return styleName;
0929: }
0930:
0931: /**
0932: * Returns the <code>n</code>th immediate <strong>visible</strong>
0933: * child <code>Component</code>.
0934: *
0935: * @param n the index of the <code>Component</code> to retrieve
0936: * @return the <code>Component</code> at index <code>n</code>
0937: * @throws IndexOutOfBoundsException when the index is invalid
0938: */
0939: public final Component getVisibleComponent(int n) {
0940: if (children == null) {
0941: throw new IndexOutOfBoundsException(Integer.toString(n));
0942: }
0943: int visibleComponentCount = 0;
0944: Component component = null;
0945: Iterator it = children.iterator();
0946: while (visibleComponentCount <= n) {
0947: if (!it.hasNext()) {
0948: throw new IndexOutOfBoundsException(Integer.toString(n));
0949: }
0950: component = (Component) it.next();
0951: if (component.isVisible()) {
0952: ++visibleComponentCount;
0953: }
0954: }
0955: return component;
0956: }
0957:
0958: /**
0959: * Returns the number of <strong>visible</strong> immediate child
0960: * <code>Component</code>s.
0961: *
0962: * @return the number of <strong>visible</strong> immediate child
0963: * <code>Component</code>s
0964: */
0965: public final int getVisibleComponentCount() {
0966: if (children == null) {
0967: return 0;
0968: } else {
0969: int visibleComponentCount = 0;
0970: Iterator it = children.iterator();
0971: while (it.hasNext()) {
0972: Component component = (Component) it.next();
0973: if (component.isVisible()) {
0974: ++visibleComponentCount;
0975: }
0976: }
0977: return visibleComponentCount;
0978: }
0979: }
0980:
0981: /**
0982: * Returns an array of all <strong>visible</strong> immediate child
0983: * <code>Component</code>s.
0984: *
0985: * @return an array of all <strong>visible</strong> immediate child
0986: * <code>Component</code>s
0987: */
0988: public final Component[] getVisibleComponents() {
0989: if (children == null) {
0990: return EMPTY_COMPONENT_ARRAY;
0991: } else {
0992: Iterator it = children.iterator();
0993: List visibleChildList = new ArrayList();
0994: while (it.hasNext()) {
0995: Component component = (Component) it.next();
0996: if (component.isVisible()) {
0997: visibleChildList.add(component);
0998: }
0999: }
1000: return (Component[]) visibleChildList
1001: .toArray(new Component[visibleChildList.size()]);
1002: }
1003: }
1004:
1005: /**
1006: * Determines if a local <code>EventListenerList</code> exists.
1007: * If no listener list exists, it can be assured that there are thus no
1008: * listeners registered to it. This method should be invoked by event
1009: * firing code prior to invoking <code>getListenerList()</code> to avoid
1010: * unnecessary creation of an <code>EventListenerList</code> in response
1011: * to their query.
1012: *
1013: * @return true if a local <code>EventListenerList</code> exists
1014: */
1015: protected boolean hasEventListenerList() {
1016: return listenerList != null;
1017: }
1018:
1019: /**
1020: * Determines the index of the given <code>Component</code> within the
1021: * children of this <code>Component</code>. If the given
1022: * <code>Component</code> is not a child, <code>-1</code> is returned.
1023: *
1024: * @param c the <code>Component</code> to analyze
1025: * @return the index of the specified <code>Component</code> amongst the
1026: * children of this <code>Component</code>
1027: */
1028: public final int indexOf(Component c) {
1029: return children == null ? -1 : children.indexOf(c);
1030: }
1031:
1032: /**
1033: * Life-cycle method invoked when the <code>Component</code> is added
1034: * to a registered hierarchy. Implementations should always invoke
1035: * <code>super.init()</code>.
1036: * Modifications to the component hierarchy are not allowed within this
1037: * method.
1038: */
1039: public void init() {
1040: }
1041:
1042: /**
1043: * Determines if this <code>Component</code> is or is an ancestor of
1044: * the specified <code>Component</code>.
1045: *
1046: * @param c the <code>Component</code> to test for ancestry
1047: * @return true if this <code>Component</code> is an ancestor of the
1048: * specified <code>Component</code>
1049: */
1050: public final boolean isAncestorOf(Component c) {
1051: while (c != null && c != this ) {
1052: c = c.parent;
1053: }
1054: return c == this ;
1055: }
1056:
1057: /**
1058: * Determines the enabled state of this <code>Component</code>.
1059: * Disabled<code>Component</code>s are not eligible to receive user input.
1060: * The application container may render disabled components with an altered
1061: * appearance.
1062: *
1063: * @return true if the component is enabled
1064: * @see #verifyInput(java.lang.String, java.lang.Object)
1065: */
1066: public final boolean isEnabled() {
1067: return (flags & FLAG_ENABLED) != 0;
1068: }
1069:
1070: /**
1071: * Determines if the <code>Component</code> participates in (tab) focus
1072: * traversal.
1073: *
1074: * @return true if the <code>Component</code> participates in focus
1075: * traversal
1076: */
1077: public boolean isFocusTraversalParticipant() {
1078: return (flags & FLAG_FOCUS_TRAVERSAL_PARTICIPANT) != 0;
1079: }
1080:
1081: /**
1082: * Determines if the <code>Component</code> is registered to an
1083: * <code>ApplicationInstance</code>.
1084: *
1085: * @return true if the <code>Component</code> is registered to an
1086: * <code>ApplicationInstance</code>
1087: */
1088: public final boolean isRegistered() {
1089: return applicationInstance != null;
1090: }
1091:
1092: /**
1093: * Determines whether this <code>Component</code> should be rendered with
1094: * an enabled state.
1095: * Disabled<code>Component</code>s are not eligible to receive user input.
1096: * The application container may render disabled components with an altered
1097: * appearance.
1098: *
1099: * @return true if the component should be rendered enabled.
1100: */
1101: public final boolean isRenderEnabled() {
1102: Component component = this ;
1103: while (component != null) {
1104: if ((component.flags & FLAG_ENABLED) == 0) {
1105: return false;
1106: }
1107: component = component.parent;
1108: }
1109: return true;
1110: }
1111:
1112: /**
1113: * Determines if the <code>Component</code> and all of its parents are
1114: * visible.
1115: *
1116: * @return true if the <code>Component</code> is recursively visible
1117: */
1118: public final boolean isRenderVisible() {
1119: Component component = this ;
1120: while (component != null) {
1121: if ((component.flags & FLAG_VISIBLE) == 0) {
1122: return false;
1123: }
1124: component = component.parent;
1125: }
1126: return true;
1127: }
1128:
1129: /**
1130: * Determines if a given <code>Component</code> is valid to be added as a
1131: * child to this <code>Component</code>. Default implementation always
1132: * returns true, may be overridden to provide specific behavior.
1133: *
1134: * @param child the <code>Component</code> to evaluate as a child
1135: * @return true if the <code>Component</code> is a valid child
1136: */
1137: public boolean isValidChild(Component child) {
1138: return true;
1139: }
1140:
1141: /**
1142: * Determines if this <code>Component</code> is valid to be added as a
1143: * child of the given parent <code>Component</code>. Default
1144: * implementation always returns true, may be overridden to provide specific
1145: * behavior.
1146: *
1147: * @param parent the <code>Component</code> to evaluate as a parent
1148: * @return true if the <code>Component</code> is a valid parent
1149: */
1150: public boolean isValidParent(Component parent) {
1151: return true;
1152: }
1153:
1154: /**
1155: * Returns the visibility state of this <code>Component</code>.
1156: * Non-visible components will not be seen by the rendering application
1157: * container, and will not be rendered in any fashion on the user
1158: * interface. Rendering Application Containers should ensure that no
1159: * information about the state of an invisible component is provided to
1160: * the user interface for security purposes.
1161: *
1162: * @return the visibility state of this <code>Component</code>
1163: */
1164: public final boolean isVisible() {
1165: return (FLAG_VISIBLE & flags) != 0;
1166: }
1167:
1168: /**
1169: * Processes client input specific to the <code>Component</code>
1170: * received from the <code>UpdateManager</code>.
1171: * Derivative implementations should take care to invoke
1172: * <code>super.processInput()</code>.
1173: *
1174: * @param inputName the name of the input
1175: * @param inputValue the value of the input
1176: * @see nextapp.echo2.app.update.UpdateManager
1177: */
1178: public void processInput(String inputName, Object inputValue) {
1179: }
1180:
1181: /**
1182: * Sets the <code>ApplicationInstance</code> to which this component is
1183: * registered.
1184: * <p>
1185: * The <code>ApplicationInstance</code> to which a component is registered
1186: * may not be changed directly from one to another, i.e., if the component
1187: * is registered to instance "A" and an attempt is made to set it to
1188: * instance "B", an <code>IllegalStateException</code> will be thrown. In
1189: * order to change the instance to which a component is registered, the
1190: * instance must first be set to null.
1191: *
1192: * @param newValue the new <code>ApplicationInstance</code>
1193: * @throws IllegalStateException in the event that an attempt is made to
1194: * re-add a <code>Component</code> to a hierarchy during a
1195: * <code>dispose()</code> operation or if an attempt is made to
1196: * remove a <code>Component</code> during an <code>init()</code>
1197: * operation.
1198: */
1199: void register(ApplicationInstance newValue) {
1200: // Verifying 'registering' flag is not set.
1201: if ((flags & FLAG_REGISTERING) != 0) {
1202: throw new IllegalStateException(
1203: "Illegal attempt to register/unregister Component from within invocation of registration change "
1204: + "life-cycle method.");
1205: }
1206: try {
1207: // Set 'registering' flag.
1208: flags |= FLAG_REGISTERING;
1209:
1210: if (applicationInstance == newValue) {
1211: // Child component added/removed during init()/dispose(): do nothing.
1212: return;
1213: }
1214:
1215: if (applicationInstance != null && newValue != null) {
1216: throw new IllegalStateException(
1217: "Illegal attempt to re-register Component to alternate ApplicationInstance.");
1218: }
1219:
1220: if (newValue == null) { // unregistering
1221: if (children != null) {
1222: Iterator it = children.iterator();
1223: while (it.hasNext()) {
1224: ((Component) it.next()).register(null); // Recursively unregister children.
1225: }
1226: }
1227:
1228: applicationInstance.unregisterComponent(this );
1229: }
1230:
1231: applicationInstance = newValue;
1232:
1233: if (newValue != null) { // registering
1234: applicationInstance.registerComponent(this );
1235:
1236: if (children != null) {
1237: Iterator it = children.iterator();
1238: while (it.hasNext()) {
1239: ((Component) it.next()).register(newValue); // Recursively register children.
1240: }
1241: }
1242: }
1243: } finally {
1244: // Clear 'registering' flag.
1245: flags &= ~FLAG_REGISTERING;
1246: }
1247: }
1248:
1249: /**
1250: * Removes the specified child <code>Component</code> from this
1251: * <code>Component</code>.
1252: * <p>
1253: * All <code>Component</code> remove operations use this method to
1254: * remove <code>Component</code>s. <code>Component</code>s that require
1255: * notification of all child removals should
1256: * override this method (while ensuring to call the superclass'
1257: * implementation).
1258: *
1259: * @param c the child <code>Component</code> to remove
1260: */
1261: public void remove(Component c) {
1262:
1263: if (children == null || !children.contains(c)) {
1264: // Do-nothing if component is not a child.
1265: return;
1266: }
1267:
1268: c.doDispose();
1269:
1270: // Deregister child.
1271: if (applicationInstance != null) {
1272: c.register(null);
1273: }
1274:
1275: // Dissolve references between parent and child.
1276: children.remove(c);
1277: c.parent = null;
1278:
1279: // Notify PropertyChangeListeners of change.
1280: firePropertyChange(CHILDREN_CHANGED_PROPERTY, c, null);
1281: }
1282:
1283: /**
1284: * Removes the <code>Component</code> at the <code>n</code>th index.
1285: *
1286: * @param n the index of the child <code>Component</code> to remove
1287: * @throws IndexOutOfBoundsException if the index is not valid
1288: */
1289: public void remove(int n) {
1290: if (children == null) {
1291: throw new IndexOutOfBoundsException();
1292: }
1293: remove(getComponent(n));
1294: }
1295:
1296: /**
1297: * Removes all child <code>Component</code>s.
1298: */
1299: public void removeAll() {
1300: if (children != null) {
1301: while (children.size() > 0) {
1302: Component c = (Component) children
1303: .get(children.size() - 1);
1304: remove(c);
1305: }
1306: children = null;
1307: }
1308: }
1309:
1310: /**
1311: * Removes a property change listener from this <code>Component</code>.
1312: *
1313: * @param l the listener to be removed
1314: */
1315: public void removePropertyChangeListener(PropertyChangeListener l) {
1316: if (propertyChangeSupport != null) {
1317: propertyChangeSupport.removePropertyChangeListener(l);
1318: }
1319: }
1320:
1321: /**
1322: * Sets the default background color of the <code>Component</code>.
1323: *
1324: * @param newValue the new background <code>Color</code>
1325: */
1326: public void setBackground(Color newValue) {
1327: setProperty(PROPERTY_BACKGROUND, newValue);
1328: }
1329:
1330: /**
1331: * Sets the enabled state of the <code>Component</code>.
1332: *
1333: * @param newValue the new state
1334: * @see #isEnabled
1335: */
1336: public void setEnabled(boolean newValue) {
1337: boolean oldValue = (flags & FLAG_ENABLED) != 0;
1338: if (oldValue != newValue) {
1339: flags ^= FLAG_ENABLED; // Toggle FLAG_ENABLED bit.
1340: firePropertyChange(ENABLED_CHANGED_PROPERTY, new Boolean(
1341: oldValue), new Boolean(newValue));
1342: }
1343: }
1344:
1345: /**
1346: * Sets the focus traversal (tab) index of the component.
1347: *
1348: * @param newValue the new focus traversal index
1349: * @see #getFocusTraversalIndex()
1350: */
1351: public void setFocusTraversalIndex(int newValue) {
1352: int oldValue = getFocusTraversalIndex();
1353: newValue &= 0x7fff;
1354: flags = flags & ((~FLAGS_FOCUS_TRAVERSAL_INDEX))
1355: | (newValue << 16);
1356: firePropertyChange(FOCUS_TRAVERSAL_INDEX_CHANGED_PROPERTY,
1357: new Integer(oldValue), new Integer(newValue));
1358: }
1359:
1360: /**
1361: * Sets whether the component participates in the focus traversal order
1362: * (tab order).
1363: *
1364: * @param newValue true if the component participates in the focus
1365: * traversal order
1366: */
1367: public void setFocusTraversalParticipant(boolean newValue) {
1368: boolean oldValue = isFocusTraversalParticipant();
1369: if (oldValue != newValue) {
1370: flags ^= FLAG_FOCUS_TRAVERSAL_PARTICIPANT; // Toggle FLAG_FOCUS_TRAVERSAL_PARTICIPANT bit.
1371: firePropertyChange(
1372: FOCUS_TRAVERSAL_PARTICIPANT_CHANGED_PROPERTY,
1373: new Boolean(oldValue), new Boolean(newValue));
1374: }
1375: }
1376:
1377: /**
1378: * Sets the default text font of the <code>Component</code>.
1379: *
1380: * @param newValue the new <code>Font</code>
1381: */
1382: public void setFont(Font newValue) {
1383: setProperty(PROPERTY_FONT, newValue);
1384: }
1385:
1386: /**
1387: * Sets the default foreground color of the <code>Component</code>.
1388: *
1389: * @param newValue the new foreground <code>Color</code>
1390: */
1391: public void setForeground(Color newValue) {
1392: setProperty(PROPERTY_FOREGROUND, newValue);
1393: }
1394:
1395: /**
1396: * Sets a user-defined identifier for this <code>Component</code>.
1397: *
1398: * @param id the new identifier
1399: */
1400: public void setId(String id) {
1401: this .id = id;
1402: }
1403:
1404: /**
1405: * Sets a generic indexed property of the <code>Component</code>.
1406: * The value will be stored in this <code>Component</code>'s local style.
1407: *
1408: * @param propertyName the name of the property
1409: * @param propertyIndex the index of the property
1410: * @param newValue the value of the property
1411: *
1412: * @see #getIndexedProperty(java.lang.String, int)
1413: */
1414: public void setIndexedProperty(String propertyName,
1415: int propertyIndex, Object newValue) {
1416: localStyle.setIndexedProperty(propertyName, propertyIndex,
1417: newValue);
1418: firePropertyChange(propertyName, null, null);
1419: }
1420:
1421: /**
1422: * Sets the <code>LayoutData</code> of this <code>Component</code>.
1423: * A <code>LayoutData</code> implementation describes how this
1424: * <code>Component</code> is laid out within/interacts with its
1425: * containing parent <code>Component</code>.
1426: *
1427: * @param newValue the new <code>LayoutData</code>
1428: * @see LayoutData
1429: */
1430: public void setLayoutData(LayoutData newValue) {
1431: setProperty(PROPERTY_LAYOUT_DATA, newValue);
1432: }
1433:
1434: /**
1435: * Sets the <code>LayoutDirection</code> of this <code>Component</code>,
1436: * describing whether content is rendered left-to-right or right-to-left.
1437: *
1438: * @param newValue the new <code>LayoutDirection</code>.
1439: */
1440: public void setLayoutDirection(LayoutDirection newValue) {
1441: LayoutDirection oldValue = layoutDirection;
1442: layoutDirection = newValue;
1443: firePropertyChange(LAYOUT_DIRECTION_CHANGED_PROPERTY, oldValue,
1444: newValue);
1445: }
1446:
1447: /**
1448: * Sets the locale of the <code>Component</code>.
1449: *
1450: * @param newValue the new locale
1451: * @see #getLocale()
1452: */
1453: public void setLocale(Locale newValue) {
1454: Locale oldValue = locale;
1455: locale = newValue;
1456: firePropertyChange(LOCALE_CHANGED_PROPERTY, oldValue, newValue);
1457: }
1458:
1459: /**
1460: * Sets a generic property of the <code>Component</code>.
1461: * The value will be stored in this <code>Component</code>'s local style.
1462: *
1463: * @param propertyName the name of the property
1464: * @param newValue the value of the property
1465: * @see #getProperty(java.lang.String)
1466: */
1467: public void setProperty(String propertyName, Object newValue) {
1468: Object oldValue = localStyle.getProperty(propertyName);
1469: localStyle.setProperty(propertyName, newValue);
1470: firePropertyChange(propertyName, oldValue, newValue);
1471: }
1472:
1473: /**
1474: * Sets a custom render identifier for this <code>Component</code>.
1475: * The identifier may be changed without notification if another
1476: * component is already using it.
1477: * Identifiers are limited to ASCII alphanumeric values.
1478: * The first character must be an upper- or lower-case ASCII letter.
1479: * Underscores and other punctuation characters are not permitted.
1480: * Use of "TitleCase" or "camelCase" is recommended.
1481: *
1482: * @param renderId the new identifier
1483: */
1484: public void setRenderId(String renderId) {
1485: if (this .renderId != null && renderId != null
1486: && this .applicationInstance != null) {
1487: throw new IllegalStateException(
1488: "Cannot set renderId while component is registered.");
1489: }
1490: if (renderId != null) {
1491: int length = renderId.length();
1492: if (!isLetter(renderId.charAt(0))) {
1493: throw new IllegalArgumentException(
1494: "Invalid identifier:" + renderId);
1495: }
1496: for (int i = 1; i < length; ++i) {
1497: if (!isLetterOrDigit(renderId.charAt(i))) {
1498: throw new IllegalArgumentException(
1499: "Invalid identifier:" + renderId);
1500: }
1501: }
1502:
1503: }
1504: assignRenderId(renderId);
1505: }
1506:
1507: /**
1508: * Sets the shared style of the <code>Component</code>.
1509: * Setting the shared style will have no impact on the local stylistic
1510: * properties of the <code>Component</code>.
1511: *
1512: * @param newValue the new shared style
1513: * @see #getStyle()
1514: */
1515: public void setStyle(Style newValue) {
1516: Style oldValue = sharedStyle;
1517: sharedStyle = newValue;
1518: firePropertyChange(STYLE_CHANGED_PROPERTY, oldValue, newValue);
1519: }
1520:
1521: /**
1522: * Sets the name of the style to use from the
1523: * <code>ApplicationInstance</code>-defined <code>StyleSheet</code>.
1524: * Setting the style name will have no impact on the local stylistic
1525: * properties of the <code>Component</code>.
1526: *
1527: * @param newValue the new style name
1528: * @see #getStyleName
1529: */
1530: public void setStyleName(String newValue) {
1531: String oldValue = styleName;
1532: styleName = newValue;
1533: firePropertyChange(STYLE_NAME_CHANGED_PROPERTY, oldValue,
1534: newValue);
1535: }
1536:
1537: /**
1538: * Sets the visibility state of this <code>Component</code>.
1539: *
1540: * @param newValue the new visibility state
1541: * @see #isVisible()
1542: */
1543: public void setVisible(boolean newValue) {
1544: boolean oldValue = (flags & FLAG_VISIBLE) != 0;
1545: if (oldValue != newValue) {
1546: flags ^= FLAG_VISIBLE; // Toggle FLAG_VISIBLE bit.
1547: firePropertyChange(VISIBLE_CHANGED_PROPERTY, new Boolean(
1548: oldValue), new Boolean(newValue));
1549: }
1550: }
1551:
1552: /**
1553: * A life-cycle method invoked before the component is rendered to ensure it
1554: * is in a valid state. Default implementation is empty. Overriding
1555: * implementations should ensure to invoke <code>super.validate()</code>
1556: * out of convention.
1557: */
1558: public void validate() {
1559: }
1560:
1561: /**
1562: * Invoked by the <code>ClientUpdateManager</code> on each component in the
1563: * hierarchy whose <code>processInput()</code> method will layer be invoked
1564: * in the current transaction. This method should return true if the
1565: * component will be capable of processing the given input in its current
1566: * state or false otherwise. This method should not do any of the actual
1567: * processing work if overridden (any actual processing should be done in
1568: * the <code>processInput()</code> implementation).
1569: * <p>
1570: * The default implementation verifies that the component is visible,
1571: * enabled, and not "obscured" by the presence of any modal component.
1572: * If overriding this method, your implementation should invoke
1573: * <code>super.verifyInput()</code>.
1574: *
1575: * @param inputName the name of the input
1576: * @param inputValue the value of the input
1577: * @return true if the input is allowed to be processed by this component
1578: * in its current state
1579: */
1580: public boolean verifyInput(String inputName, Object inputValue) {
1581: if (applicationInstance != null
1582: && !applicationInstance.verifyModalContext(this )) {
1583: return false;
1584: }
1585: return isVisible() && isEnabled();
1586: }
1587:
1588: /**
1589: * Determines the index of the given <code>Component</code> within the
1590: * <strong>visible</strong> children of this <code>Component</code>. If the
1591: * given <code>Component</code> is not a child, <code>-1</code> is
1592: * returned.
1593: *
1594: * @param c the <code>Component</code> to analyze
1595: * @return the index of the specified <code>Component</code> amongst the
1596: * <strong>visible</strong> children of this <code>Component</code>
1597: */
1598: public final int visibleIndexOf(Component c) {
1599: if (!c.isVisible()) {
1600: return -1;
1601: }
1602: if (children == null) {
1603: return -1;
1604: }
1605: int visibleIndex = 0;
1606: Iterator it = children.iterator();
1607: while (it.hasNext()) {
1608: Component component = (Component) it.next();
1609: if (!component.isVisible()) {
1610: continue;
1611: }
1612: if (component.equals(c)) {
1613: return visibleIndex;
1614: }
1615: ++visibleIndex;
1616: }
1617: return -1;
1618: }
1619: }
|