0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.form;
0043:
0044: import java.beans.*;
0045: import java.util.*;
0046: import java.lang.reflect.Method;
0047: import org.netbeans.modules.form.RADProperty.FakePropertyDescriptor;
0048:
0049: import org.openide.*;
0050: import org.openide.nodes.*;
0051: import org.openide.util.Lookup;
0052: import org.openide.util.NbBundle;
0053: import org.openide.util.datatransfer.NewType;
0054:
0055: import org.netbeans.modules.form.codestructure.*;
0056:
0057: /**
0058: *
0059: * @author Ian Formanek
0060: */
0061:
0062: public class RADComponent {
0063:
0064: // -----------------------------------------------------------------------------
0065: // Static variables
0066:
0067: public static final String PROP_NAME = "variableName"; // NOI18N
0068:
0069: static final NewType[] NO_NEW_TYPES = {};
0070: static final RADProperty[] NO_PROPERTIES = {};
0071: static final BindingProperty[] NO_BINDINGS = {};
0072:
0073: // -----------------------------------------------------------------------------
0074: // Private variables
0075:
0076: private static int idCounter;
0077:
0078: private String id = Integer.toString(++idCounter);
0079:
0080: private Class<? extends Object> beanClass;
0081: private Object beanInstance;
0082: private BeanInfo beanInfo;
0083: private BeanInfo fakeBeanInfo;
0084: private String missingClassName;
0085:
0086: protected Node.PropertySet[] propertySets;
0087: private Node.Property[] syntheticProperties;
0088: private RADProperty[] beanProperties1;
0089: private RADProperty[] beanProperties2;
0090: private BindingProperty[][] bindingProperties;
0091: private EventProperty[] eventProperties;
0092: private Map<Object, RADProperty[]> otherProperties;
0093: private List actionProperties;
0094:
0095: private RADProperty[] knownBeanProperties;
0096: private Event[] knownEvents; // must be grouped by EventSetDescriptor
0097:
0098: private PropertyChangeListener propertyListener;
0099:
0100: private Map<String, Object> auxValues;
0101: protected Map<String, Node.Property> nameToProperty;
0102:
0103: private RADComponent parentComponent;
0104:
0105: private FormModel formModel;
0106: private boolean inModel;
0107:
0108: private RADComponentNode componentNode;
0109:
0110: private CodeExpression componentCodeExpression;
0111:
0112: private String storedName; // component name preserved e.g. for remove undo
0113:
0114: private boolean valid = true;
0115:
0116: // -----------------------------------------------------------------------------
0117: // Constructors & Initialization
0118:
0119: /**
0120: * Called to initialize the component with specified FormModel.
0121: *
0122: * @param formModel the FormModel of the form into which this component
0123: * will be added
0124: * @return <code>true</code> if the model was initialized,
0125: * <code>false</code> otherwise.
0126: */
0127: public boolean initialize(FormModel formModel) {
0128: if (this .formModel == null) {
0129: this .formModel = formModel;
0130:
0131: // properties and events will be created on first request
0132: clearProperties();
0133:
0134: // if (beanClass != null)
0135: // createCodeExpression();
0136:
0137: return true;
0138: } else if (this .formModel != formModel)
0139: throw new IllegalStateException(
0140: "Cannot initialize metacomponent with another form model"); // NOI18N
0141: return false;
0142: }
0143:
0144: public void setParentComponent(RADComponent parentComp) {
0145: parentComponent = parentComp;
0146: }
0147:
0148: /** Initializes the bean instance represented by this meta component.
0149: * A default instance is created for the given bean class.
0150: * The meta component is fully initialized after this method returns.
0151: *
0152: * @param beanClass the bean class to be represented by this meta component
0153: * @return initialized instance.
0154: * @throws java.lang.Exception when the instance cannot be initialized.
0155: */
0156: public Object initInstance(Class<? extends Object> beanClass)
0157: throws Exception {
0158: if (beanClass == null)
0159: throw new NullPointerException();
0160:
0161: if (this .beanClass != beanClass && this .beanClass != null) {
0162: beanInfo = null;
0163: fakeBeanInfo = null;
0164: clearProperties();
0165: }
0166:
0167: this .beanClass = beanClass;
0168:
0169: Object bean = createBeanInstance();
0170: getBeanInfo(); // force BeanInfo creation here - will be needed, may fail
0171: setBeanInstance(bean);
0172:
0173: return beanInstance;
0174: }
0175:
0176: /** Sets the bean instance represented by this meta component.
0177: * The meta component is fully initialized after this method returns.
0178: * @param beanInstance the bean to be represented by this meta component
0179: */
0180: public void setInstance(Object beanInstance) {
0181: if (this .beanClass != beanInstance.getClass()) {
0182: beanInfo = null;
0183: fakeBeanInfo = null;
0184: }
0185:
0186: clearProperties();
0187:
0188: this .beanClass = beanInstance.getClass();
0189:
0190: getBeanInfo(); // force BeanInfo creation here - will be needed, may fail
0191: setBeanInstance(beanInstance);
0192:
0193: // commented out: since we don't hold default instances separately, we can't
0194: // reinstate the bean properties against the global default property values
0195: // getAllBeanProperties();
0196: // for (int i=0; i < knownBeanProperties.length; i++) {
0197: // try {
0198: // knownBeanProperties[i].reinstateProperty();
0199: // }
0200: // catch (Exception ex) {
0201: // ErrorManager.getDefault()
0202: // .notify(ErrorManager.INFORMATIONAL, ex);
0203: // }
0204: // }
0205: }
0206:
0207: /** Updates the bean instance - e.g. when setting a property requires
0208: * to create new instance of the bean.
0209: *
0210: * @param beanInstance bean instance.
0211: */
0212: public void updateInstance(Object beanInstance) {
0213: if (this .beanInstance != null
0214: && this .beanClass == beanInstance.getClass())
0215: setBeanInstance(beanInstance);
0216: // should properties also be reinstated?
0217: // formModel.fireFormChanged() ?
0218: else
0219: setInstance(beanInstance);
0220: }
0221:
0222: /**
0223: * Called to create the instance of the bean. This method is called if the
0224: * initInstance method is used; using the setInstance method, the bean
0225: * instance is set directly.
0226: *
0227: * @return the instance of the bean that will be used during design time
0228: * @throws java.lang.Exception when the instance cannot be created.
0229: */
0230: protected Object createBeanInstance() throws Exception {
0231: return CreationFactory.createDefaultInstance(beanClass);
0232: }
0233:
0234: /** Sets directly the bean instance. Can be overriden.
0235: *
0236: * @param beanInstance bean instance.
0237: */
0238: protected void setBeanInstance(Object beanInstance) {
0239: if (beanClass == null) { // bean class not set yet
0240: beanClass = beanInstance.getClass();
0241: // createCodeExpression();
0242: }
0243: this .beanInstance = beanInstance;
0244: }
0245:
0246: void setInModel(boolean in) {
0247: if (inModel != in) {
0248: inModel = in;
0249: formModel.updateMapping(this , in);
0250: if (in) {
0251: createCodeExpression();
0252: } else {
0253: releaseCodeExpression();
0254: setNodeReference(null);
0255: }
0256: }
0257: }
0258:
0259: void setNodeReference(RADComponentNode node) {
0260: this .componentNode = node;
0261: }
0262:
0263: protected void createCodeExpression() {
0264: // create expression object
0265: if (componentCodeExpression == null) {
0266: CodeStructure codeStructure = formModel.getCodeStructure();
0267: componentCodeExpression = codeStructure
0268: .createExpression(FormCodeSupport
0269: .createOrigin(this ));
0270: codeStructure.registerExpression(componentCodeExpression);
0271: }
0272: // make sure a variable is attached
0273: if (formModel.getTopRADComponent() != this
0274: && componentCodeExpression.getVariable() == null) {
0275: formModel
0276: .getCodeStructure()
0277: .createVariableForExpression(
0278: componentCodeExpression,
0279: 0x30DF, // default type
0280: (String) getAuxValue("JavaCodeGenerator_TypeParameters"), //NOI18N
0281: storedName);
0282: }
0283: }
0284:
0285: /* final void removeCodeExpression() {
0286: if (componentCodeExpression != null) {
0287: CodeVariable var = componentCodeExpression.getVariable();
0288: if (var != null)
0289: storedName = var.getName();
0290: CodeStructure.removeExpression(componentCodeExpression);
0291: }
0292: } */
0293:
0294: final void releaseCodeExpression() {
0295: if (componentCodeExpression != null) {
0296: CodeVariable var = componentCodeExpression.getVariable();
0297: if (var != null) {
0298: storedName = var.getName();
0299: formModel.getCodeStructure()
0300: .removeExpressionFromVariable(
0301: componentCodeExpression);
0302: }
0303: }
0304: }
0305:
0306: // -----------------------------------------------------------------------------
0307: // Public interface
0308:
0309: private boolean testMode = Boolean
0310: .getBoolean("netbeans.form.layout_test"); // NOI18N
0311:
0312: public final String getId() {
0313: return testMode ? getName() : id;
0314: }
0315:
0316: public final boolean isReadOnly() {
0317: return formModel.isReadOnly(); //readOnly;
0318: }
0319:
0320: /** Provides access to the Class of the bean represented by this RADComponent
0321: * @return the Class of the bean represented by this RADComponent
0322: */
0323: public final Class<? extends Object> getBeanClass() {
0324: return beanClass;
0325: }
0326:
0327: public final String getMissingClassName() {
0328: return missingClassName;
0329: }
0330:
0331: public final void setMissingClassName(String className) {
0332: missingClassName = className;
0333: }
0334:
0335: /** Provides access to the real instance of the bean represented by this RADComponent
0336: * @return the instance of the bean represented by this RADComponent
0337: */
0338: public final Object getBeanInstance() {
0339: return beanInstance;
0340: }
0341:
0342: public final RADComponent getParentComponent() {
0343: return parentComponent;
0344: }
0345:
0346: public final boolean isParentComponent(RADComponent comp) {
0347: if (comp == null)
0348: return false;
0349:
0350: do {
0351: comp = comp.getParentComponent();
0352: if (comp == this )
0353: return true;
0354: } while (comp != null);
0355:
0356: return false;
0357: }
0358:
0359: public Object cloneBeanInstance(
0360: Collection<RADProperty> relativeProperties) {
0361: Object clone;
0362: try {
0363: clone = createBeanInstance();
0364: } catch (Exception ex) { // ignore, this should not fail
0365: org.openide.ErrorManager.getDefault().notify(
0366: org.openide.ErrorManager.INFORMATIONAL, ex);
0367: return null;
0368: }
0369:
0370: FormUtils.copyPropertiesToBean(getKnownBeanProperties(), clone,
0371: relativeProperties);
0372: return clone;
0373: }
0374:
0375: /** Provides access to BeanInfo of the bean represented by this RADComponent
0376: * @return the BeanInfo of the bean represented by this RADComponent
0377: */
0378: public BeanInfo getBeanInfo() {
0379: if (beanInfo == null) {
0380: try {
0381: beanInfo = FormUtils.getBeanInfo(beanClass);
0382: } catch (Error err) { // Issue 74002
0383: org.openide.ErrorManager.getDefault().notify(
0384: org.openide.ErrorManager.INFORMATIONAL, err);
0385: beanInfo = new FakeBeanInfo();
0386: } catch (IntrospectionException ex) {
0387: org.openide.ErrorManager.getDefault().notify(
0388: org.openide.ErrorManager.INFORMATIONAL, ex);
0389: beanInfo = new FakeBeanInfo();
0390: }
0391: }
0392: if (isValid()) {
0393: return beanInfo;
0394: } else {
0395: if (fakeBeanInfo == null) {
0396: fakeBeanInfo = new FakeBeanInfo();
0397: }
0398: return fakeBeanInfo;
0399: }
0400: }
0401:
0402: /** This method can be used to check whether the bean represented by this
0403: * RADComponent has hidden-state.
0404: * @return true if the component has hidden state, false otherwise
0405: */
0406: public boolean hasHiddenState() {
0407: String name = beanClass.getName();
0408: if (name.startsWith("javax.") // NOI18N
0409: || name.startsWith("java.") // NOI18N
0410: || name.startsWith("org.openide.")) // NOI18N
0411: return false;
0412:
0413: return getBeanInfo().getBeanDescriptor().getValue(
0414: "hidden-state") != null; // NOI18N
0415: }
0416:
0417: public CodeExpression getCodeExpression() {
0418: return componentCodeExpression;
0419: }
0420:
0421: /** Getter for the name of the metacomponent - it maps to variable name
0422: * declared for the instance of the component in the generated java code.
0423: * It is a unique identification of the component within a form, but it may
0424: * change (currently editable as "Variable Name" in code gen. properties).
0425: * @return current value of the Name property
0426: */
0427: public String getName() {
0428: if (componentCodeExpression != null) {
0429: CodeVariable var = componentCodeExpression.getVariable();
0430: if (var != null)
0431: return var.getName();
0432: }
0433: return storedName;
0434: }
0435:
0436: /** Setter for the name of the component - it is the name of the
0437: * component's node and the name of the variable declared for the component
0438: * in the generated code.
0439: *
0440: * @param name new name of the component
0441: */
0442: public void setName(String name) {
0443: if (!needsVariableRename(name)) {
0444: return;
0445: }
0446:
0447: String oldName = componentCodeExpression.getVariable()
0448: .getName();
0449:
0450: formModel.getCodeStructure().renameVariable(oldName, name);
0451:
0452: resourceComponentRename(oldName, name);
0453:
0454: renameDefaultEventHandlers(oldName, name);
0455: // [possibility of renaming default event handlers should be probably
0456: // configurable in options]
0457:
0458: formModel.fireSyntheticPropertyChanged(this , PROP_NAME,
0459: oldName, name);
0460:
0461: if (getNodeReference() != null)
0462: getNodeReference().updateName();
0463: }
0464:
0465: /**
0466: * Method used to push setName through refactoring instead of just setting the variable name and
0467: * not changing it in users custom code.
0468: * @param name the new name for the variable
0469: */
0470: public void rename(String name) {
0471: if (!needsVariableRename(name)) {
0472: return;
0473: }
0474:
0475: if (!org.openide.util.Utilities.isJavaIdentifier(name)) {
0476: IllegalArgumentException iae = new IllegalArgumentException(
0477: "Invalid component name"); // NOI18N
0478: ErrorManager
0479: .getDefault()
0480: .annotate(
0481: iae,
0482: ErrorManager.USER,
0483: null,
0484: FormUtils
0485: .getBundleString("ERR_INVALID_COMPONENT_NAME"), // NOI18N
0486: null, null);
0487: throw iae;
0488: }
0489:
0490: if (formModel.getCodeStructure().isVariableNameReserved(name)) {
0491: IllegalArgumentException iae = new IllegalArgumentException(
0492: "Component name already in use: " + name); // NOI18N
0493: ErrorManager
0494: .getDefault()
0495: .annotate(
0496: iae,
0497: ErrorManager.USER,
0498: null,
0499: FormUtils
0500: .getBundleString("ERR_COMPONENT_NAME_ALREADY_IN_USE"), // NOI18N
0501: null, null);
0502: throw iae;
0503: }
0504:
0505: try {
0506: RADComponentRenameRefactoringSupport rf = new RADComponentRenameRefactoringSupport(
0507: name);
0508: rf.setComponent(this );
0509: rf.doRenameRefactoring();
0510: } finally { // hack for robustness - if refactoring fails for whatever reason
0511: if (!getName().equals(name)) {
0512: setName(name);
0513: }
0514: }
0515: }
0516:
0517: private boolean needsVariableRename(String name) {
0518: if (componentCodeExpression != null) {
0519: CodeVariable var = componentCodeExpression.getVariable();
0520: return var != null && name != null
0521: && !name.equals(var.getName());
0522: }
0523: return false;
0524: }
0525:
0526: public void setStoredName(String name) {
0527: storedName = name;
0528: }
0529:
0530: private void renameDefaultEventHandlers(String oldComponentName,
0531: String newComponentName) {
0532: boolean renamed = false; // whether any handler was renamed
0533: FormEvents formEvents = null;
0534:
0535: Event[] events = getKnownEvents();
0536: for (int i = 0; i < events.length; i++) {
0537: String[] handlers = events[i].getEventHandlers();
0538: for (int j = 0; j < handlers.length; j++) {
0539: String handlerName = handlers[j];
0540: int idx = handlerName.indexOf(oldComponentName);
0541: if (idx >= 0) {
0542: if (formEvents == null)
0543: formEvents = getFormModel().getFormEvents();
0544: String newHandlerName = formEvents
0545: .findFreeHandlerName(handlerName.substring(
0546: 0, idx)
0547: + newComponentName
0548: + handlerName
0549: .substring(idx
0550: + oldComponentName
0551: .length()));
0552: formEvents.renameEventHandler(handlerName,
0553: newHandlerName);
0554: }
0555: }
0556: }
0557:
0558: if (renamed && getNodeReference() != null)
0559: getNodeReference().fireComponentPropertySetsChange();
0560: }
0561:
0562: /** Allows to add an auxiliary <name, value> pair, which is persistent
0563: * in Gandalf. The current value can be obtained using
0564: * getAuxValue(aux_property_name) method. To remove aux value for specified
0565: * property name, use setAuxValue(name, null).
0566: * @param key name of the aux property
0567: * @param value new value of the aux property or null to remove it
0568: */
0569: public void setAuxValue(String key, Object value) {
0570: if (auxValues == null) {
0571: if (value == null)
0572: return;
0573: auxValues = new TreeMap<String, Object>();
0574: }
0575: auxValues.put(key, value);
0576: }
0577:
0578: /** Allows to obtain an auxiliary value for specified aux property name.
0579: * @param key name of the aux property
0580: * @return null if the aux value for specified name is not set
0581: */
0582: public Object getAuxValue(String key) {
0583: return auxValues != null ? auxValues.get(key) : null;
0584: }
0585:
0586: /** Provides access to the FormModel class which manages the form in which
0587: * this component has been added.
0588: * @return the FormModel which manages the form into which this component
0589: * has been added
0590: */
0591: public final FormModel getFormModel() {
0592: return formModel;
0593: }
0594:
0595: public final boolean isInModel() {
0596: return inModel;
0597: }
0598:
0599: /** @return the map of all component's aux value-pairs of <String, Object>
0600: */
0601: public Map<String, Object> getAuxValues() {
0602: return auxValues;
0603: }
0604:
0605: /** Support for new types that can be created in this node.
0606: * @return array of new type operations that are allowed
0607: */
0608: public NewType[] getNewTypes() {
0609: return NO_NEW_TYPES;
0610: }
0611:
0612: public Node.PropertySet[] getProperties() {
0613: if (propertySets == null) {
0614: List<Node.PropertySet> propSets = new ArrayList<Node.PropertySet>(
0615: 5);
0616: createPropertySets(propSets);
0617: propertySets = new Node.PropertySet[propSets.size()];
0618: propSets.toArray(propertySets);
0619: }
0620: return propertySets;
0621: }
0622:
0623: public RADProperty[] getAllBeanProperties() {
0624: if (knownBeanProperties == null) {
0625: createBeanProperties();
0626: }
0627:
0628: return knownBeanProperties;
0629: }
0630:
0631: public RADProperty[] getKnownBeanProperties() {
0632: return knownBeanProperties != null ? knownBeanProperties
0633: : NO_PROPERTIES;
0634: }
0635:
0636: public Iterator<RADProperty> getBeanPropertiesIterator(
0637: FormProperty.Filter filter, boolean fromAll) {
0638: return new PropertyIterator<RADProperty>(
0639: fromAll ? getAllBeanProperties()
0640: : getKnownBeanProperties(), filter);
0641: }
0642:
0643: public BindingProperty[] getAllBindingProperties() {
0644: BindingProperty[][] bprop = getBindingProperties();
0645: BindingProperty[] prop = new BindingProperty[bprop[0].length
0646: + bprop[1].length + bprop[2].length];
0647: System.arraycopy(bprop[0], 0, prop, 0, bprop[0].length);
0648: System.arraycopy(bprop[1], 0, prop, bprop[0].length,
0649: bprop[1].length);
0650: System.arraycopy(bprop[2], 0, prop, bprop[0].length
0651: + bprop[1].length, bprop[2].length);
0652: return prop;
0653: }
0654:
0655: public BindingProperty[][] getBindingProperties() {
0656: if (bindingProperties == null) {
0657: createBindingProperties();
0658: }
0659: return bindingProperties;
0660: }
0661:
0662: public final BindingProperty getBindingProperty(String name) {
0663: for (BindingProperty prop : getAllBindingProperties()) {
0664: if (prop.getName().equals(name))
0665: return prop;
0666: }
0667: return null;
0668: }
0669:
0670: BindingProperty[] getKnownBindingProperties() {
0671: return bindingProperties != null ? getAllBindingProperties()
0672: : NO_BINDINGS;
0673: }
0674:
0675: boolean hasBindings() {
0676: if (bindingProperties != null) {
0677: for (BindingProperty p : getAllBindingProperties()) {
0678: if (p.getValue() != null)
0679: return true;
0680: }
0681: }
0682: return false;
0683: }
0684:
0685: /** Provides access to the Node which represents this RADComponent
0686: * @return the RADComponentNode which represents this RADComponent
0687: */
0688: public RADComponentNode getNodeReference() {
0689: return componentNode;
0690: }
0691:
0692: // -----------------------------------------------------------------------------
0693: // Access to component Properties
0694:
0695: Node.Property[] getSyntheticProperties() {
0696: if (syntheticProperties == null)
0697: syntheticProperties = createSyntheticProperties();
0698: return syntheticProperties;
0699: }
0700:
0701: RADProperty[] getBeanProperties1() {
0702: if (beanProperties1 == null)
0703: createBeanProperties();
0704: return beanProperties1;
0705: }
0706:
0707: RADProperty[] getBeanProperties2() {
0708: if (beanProperties2 == null)
0709: createBeanProperties();
0710: return beanProperties2;
0711: }
0712:
0713: EventProperty[] getEventProperties() {
0714: if (eventProperties == null)
0715: createEventProperties();
0716: return eventProperties;
0717: }
0718:
0719: List getActionProperties() {
0720: if (actionProperties == null) {
0721: createBeanProperties();
0722: }
0723: return actionProperties;
0724: }
0725:
0726: protected <T> T getPropertyByName(String name,
0727: Class<? extends T> propertyType, boolean fromAll) {
0728: Node.Property prop = nameToProperty.get(name);
0729: if (prop == null && fromAll) {
0730: if (beanProperties1 == null && !name.startsWith("$")) // NOI18N
0731: createBeanProperties();
0732: if (eventProperties == null && name.startsWith("$")) // NOI18N
0733: createEventProperties();
0734:
0735: prop = nameToProperty.get(name);
0736: }
0737: return prop != null
0738: && (propertyType == null || propertyType
0739: .isAssignableFrom(prop.getClass())) ? (T) prop
0740: : null;
0741: }
0742:
0743: /**
0744: * Returns property of given name corresponding to a property or event.
0745: * Forces creation of all property objects.
0746: *
0747: * @param name name of the property.
0748: * @return bean or event property
0749: */
0750: public Node.Property getPropertyByName(String name) {
0751: return getPropertyByName(name, null, true);
0752: }
0753:
0754: public final RADProperty getBeanProperty(String name) {
0755: return getPropertyByName(name, RADProperty.class, true);
0756: }
0757:
0758: public final Node.Property getSyntheticProperty(String name) {
0759: for (Node.Property prop : getSyntheticProperties()) {
0760: if (prop.getName().equals(name))
0761: return prop;
0762: }
0763: return null;
0764: }
0765:
0766: public RADProperty[] getFakeBeanProperties(String[] propNames,
0767: Class[] propertyTypes) {
0768: FakeBeanInfo fbi = (FakeBeanInfo) getBeanInfo();
0769: fbi.removePropertyDescriptors();
0770: for (int i = 0; i < propNames.length; i++) {
0771: fbi.addPropertyDescriptor(propNames[i], propertyTypes[i]);
0772: }
0773: return getBeanProperties(propNames);
0774: }
0775:
0776: /**
0777: * Returns bean properties of given names. Creates the properties if not
0778: * created yet, but does not force creation of all bean properties.
0779: *
0780: * @param propNames property names.
0781: * @return array of properties corresponding to the names; may contain
0782: * null if there is no property of given name
0783: */
0784: public RADProperty[] getBeanProperties(String[] propNames) {
0785: RADProperty[] properties = new RADProperty[propNames.length];
0786: PropertyDescriptor[] descriptors = null;
0787:
0788: boolean empty = knownBeanProperties == null;
0789: int validCount = 0;
0790: List<RADProperty> newProps = null;
0791: Object[] propAccessClsf = null;
0792: Object[] propParentChildDepClsf = null;
0793:
0794: int descIndex = 0;
0795: for (int i = 0; i < propNames.length; i++) {
0796: String name = propNames[i];
0797: if (name == null)
0798: continue;
0799:
0800: RADProperty prop;
0801: if (!empty) {
0802: Object obj = nameToProperty.get(name);
0803: prop = obj instanceof RADProperty ? (RADProperty) obj
0804: : null;
0805: } else
0806: prop = null;
0807:
0808: if (prop == null) {
0809: if (descriptors == null) {
0810: descriptors = getBeanInfo()
0811: .getPropertyDescriptors();
0812: if (descriptors.length == 0)
0813: break;
0814: }
0815: int j = descIndex;
0816: do {
0817: if (descriptors[j].getName().equals(name)) {
0818: if (propAccessClsf == null) {
0819: propAccessClsf = FormUtils
0820: .getPropertiesAccessClsf(beanClass);
0821: propParentChildDepClsf = FormUtils
0822: .getPropertiesParentChildDepsClsf(beanClass);
0823: }
0824:
0825: prop = createBeanProperty(descriptors[j],
0826: propAccessClsf, propParentChildDepClsf);
0827:
0828: if (!empty) {
0829: if (newProps == null)
0830: newProps = new ArrayList<RADProperty>();
0831: newProps.add(prop);
0832: }
0833: descIndex = j + 1;
0834: if (descIndex == descriptors.length
0835: && i + 1 < propNames.length)
0836: descIndex = 0; // go again from beginning
0837: break;
0838: }
0839: j++;
0840: if (j == descriptors.length)
0841: j = 0;
0842: } while (j != descIndex);
0843: }
0844: if (prop != null) {
0845: properties[i] = prop;
0846: validCount++;
0847: } else { // force all properties creation, there might be more
0848: // properties than from BeanInfo [currently just ButtonGroup]
0849: properties[i] = getPropertyByName(name,
0850: RADProperty.class, true);
0851: empty = false;
0852: newProps = null;
0853: }
0854: }
0855:
0856: if (empty) {
0857: if (validCount == properties.length)
0858: knownBeanProperties = properties;
0859: else if (validCount > 0) {
0860: knownBeanProperties = new RADProperty[validCount];
0861: for (int i = 0, j = 0; i < properties.length; i++)
0862: if (properties[i] != null)
0863: knownBeanProperties[j++] = properties[i];
0864: }
0865: } else if (newProps != null) { // just for consistency, should not occur
0866: RADProperty[] knownProps = new RADProperty[knownBeanProperties.length
0867: + newProps.size()];
0868: System.arraycopy(knownBeanProperties, 0, knownProps, 0,
0869: knownBeanProperties.length);
0870: for (int i = 0; i < newProps.size(); i++)
0871: knownProps[i + knownBeanProperties.length] = newProps
0872: .get(i);
0873:
0874: knownBeanProperties = knownProps;
0875: }
0876:
0877: return properties;
0878: }
0879:
0880: public Event getEvent(String name) {
0881: Object prop = nameToProperty.get(name);
0882: if (prop == null && eventProperties == null) {
0883: createEventProperties();
0884: prop = nameToProperty.get(name);
0885: }
0886: return prop instanceof EventProperty ? ((EventProperty) prop)
0887: .getEvent() : null;
0888: }
0889:
0890: public Event[] getEvents(String[] eventNames) {
0891: Event[] events = new Event[eventNames.length];
0892: EventSetDescriptor[] eventSets = null;
0893:
0894: boolean empty = knownEvents == null;
0895: int validCount = 0;
0896: List<Event> newEvents = null;
0897:
0898: int setIndex = 0;
0899: int methodIndex = 0;
0900:
0901: for (int i = 0; i < eventNames.length; i++) {
0902: String name = eventNames[i];
0903: if (name == null)
0904: continue;
0905:
0906: boolean fullName = name.startsWith("$"); // NOI18N
0907:
0908: Event event;
0909: if (!empty) {
0910: Object obj = nameToProperty.get(name);
0911: event = obj instanceof EventProperty ? ((EventProperty) obj)
0912: .getEvent()
0913: : null;
0914: } else
0915: event = null;
0916:
0917: if (event == null) {
0918: if (eventSets == null) {
0919: eventSets = getBeanInfo().getEventSetDescriptors();
0920: if (eventSets.length == 0)
0921: break;
0922: }
0923: int j = setIndex;
0924: do {
0925: Method[] methods = eventSets[j]
0926: .getListenerMethods();
0927: if (methods.length > 0) {
0928: int k = methodIndex;
0929: do {
0930: String eventFullId = FormEvents
0931: .getEventIdName(methods[k]);
0932: String eventId = fullName ? eventFullId
0933: : methods[k].getName();
0934: if (eventId.equals(name)) {
0935: event = createEventProperty(
0936: eventFullId, eventSets[j],
0937: methods[k]).getEvent();
0938: if (!empty) {
0939: if (newEvents == null)
0940: newEvents = new ArrayList<Event>();
0941: newEvents.add(event);
0942: }
0943: methodIndex = k + 1;
0944: break;
0945: }
0946: k++;
0947: if (k == methods.length)
0948: k = 0;
0949: } while (k != methodIndex);
0950: }
0951:
0952: if (event != null) {
0953: if (methodIndex == methods.length) {
0954: methodIndex = 0;
0955: setIndex = j + 1; // will continue in new set
0956: if (setIndex == eventSets.length
0957: && i + 1 < eventNames.length)
0958: setIndex = 0; // go again from beginning
0959: } else
0960: setIndex = j; // will continue in the same set
0961: break;
0962: }
0963:
0964: j++;
0965: if (j == eventSets.length)
0966: j = 0;
0967: methodIndex = 0;
0968: } while (j != setIndex);
0969: }
0970: if (event != null) {
0971: events[i] = event;
0972: validCount++;
0973: }
0974: }
0975:
0976: if (empty) {
0977: if (validCount == events.length)
0978: knownEvents = events;
0979: else if (validCount > 0) {
0980: knownEvents = new Event[validCount];
0981: for (int i = 0, j = 0; i < events.length; i++)
0982: if (events[i] != null)
0983: knownEvents[j++] = events[i];
0984: }
0985: } else if (newEvents != null) { // just for consistency, should not occur
0986: Event[] known = new Event[knownEvents.length
0987: + newEvents.size()];
0988: System.arraycopy(knownEvents, 0, known, 0,
0989: knownEvents.length);
0990: for (int i = 0; i < newEvents.size(); i++)
0991: known[i + knownEvents.length] = newEvents.get(i);
0992:
0993: knownEvents = known;
0994: }
0995:
0996: return events;
0997: }
0998:
0999: /** @return all events of the component grouped by EventSetDescriptor
1000: */
1001: public Event[] getAllEvents() {
1002: if (knownEvents == null || eventProperties == null) {
1003: if (eventProperties == null)
1004: createEventProperties();
1005: else {
1006: knownEvents = new Event[eventProperties.length];
1007: for (int i = 0; i < eventProperties.length; i++)
1008: knownEvents[i] = eventProperties[i].getEvent();
1009: }
1010: }
1011:
1012: return knownEvents;
1013: }
1014:
1015: // Note: events must be grouped by EventSetDescriptor
1016: public Event[] getKnownEvents() {
1017: return knownEvents != null ? knownEvents : FormEvents.NO_EVENTS;
1018: }
1019:
1020: // ---------
1021: // events
1022:
1023: Event getDefaultEvent() {
1024: int eventIndex = getBeanInfo().getDefaultEventIndex();
1025: if (eventIndex < getEventProperties().length && eventIndex >= 0) {
1026: return eventProperties[eventIndex].getEvent();
1027: } else {
1028: for (int i = 0; i < eventProperties.length; i++) {
1029: Event e = eventProperties[i].getEvent();
1030: if ("actionPerformed".equals(e.getListenerMethod()
1031: .getName()) // NOI18N
1032: && !(getBeanInstance() instanceof javax.swing.JMenu)) {
1033: return e;
1034: }
1035: }
1036: }
1037: return null;
1038: }
1039:
1040: void attachDefaultEvent() {
1041: Event event = getDefaultEvent();
1042: if (event != null) {
1043: getFormModel().getFormEvents().attachEvent(event, null,
1044: null);
1045: }
1046: }
1047:
1048: // -----------------------------------------------------------------------------
1049: // Properties
1050:
1051: protected void clearProperties() {
1052: if (nameToProperty != null)
1053: nameToProperty.clear();
1054: else
1055: nameToProperty = new HashMap<String, Node.Property>();
1056:
1057: propertySets = null;
1058: syntheticProperties = null;
1059: beanProperties1 = null;
1060: beanProperties2 = null;
1061: knownBeanProperties = null;
1062: eventProperties = null;
1063: knownEvents = null;
1064: }
1065:
1066: protected void createPropertySets(List<Node.PropertySet> propSets) {
1067: if (beanProperties1 == null)
1068: createBeanProperties();
1069:
1070: ResourceBundle bundle = FormUtils.getBundle();
1071:
1072: Node.PropertySet ps;
1073: propSets.add(new Node.PropertySet("properties", // NOI18N
1074: bundle.getString("CTL_PropertiesTab"), // NOI18N
1075: bundle.getString("CTL_PropertiesTabHint")) // NOI18N
1076: {
1077: public Node.Property[] getProperties() {
1078: return getBeanProperties1();
1079: }
1080: });
1081:
1082: if (isValid()) {
1083: Iterator entries = otherProperties.entrySet().iterator();
1084: while (entries.hasNext()) {
1085: Map.Entry entry = (Map.Entry) entries.next();
1086: final String category = (String) entry.getKey();
1087: ps = new Node.PropertySet(category, category, category) {
1088: public Node.Property[] getProperties() {
1089: if (otherProperties == null) {
1090: createBeanProperties();
1091: }
1092: return (Node.Property[]) otherProperties
1093: .get(category);
1094: }
1095: };
1096: //ps.setValue("tabName", category); // NOI18N
1097: propSets.add(ps);
1098: }
1099:
1100: if (beanProperties2.length > 0) {
1101: propSets.add(new Node.PropertySet("properties2", // NOI18N
1102: bundle.getString("CTL_Properties2Tab"), // NOI18N
1103: bundle.getString("CTL_Properties2TabHint")) // NOI18N
1104: {
1105: public Node.Property[] getProperties() {
1106: return getBeanProperties2();
1107: }
1108: });
1109: }
1110:
1111: if (getAllBindingProperties().length > 0) {
1112: BindingProperty[][] bprop = getBindingProperties();
1113: for (int i = 0; i < bprop.length; i++) {
1114: final int index = i;
1115: ps = new Node.PropertySet("binding" + i, // NOI18N
1116: bundle.getString("CTL_BindingTab" + i), // NOI18N
1117: bundle.getString("CTL_BindingTabHint" + i)) // NOI18N
1118: {
1119: public Node.Property[] getProperties() {
1120: return getBindingProperties()[index];
1121: }
1122: };
1123: ps.setValue("tabName", bundle
1124: .getString("CTL_BindingTab")); // NOI18N
1125: propSets.add(ps);
1126: }
1127: }
1128:
1129: ps = new Node.PropertySet("events", // NOI18N
1130: bundle.getString("CTL_EventsTab"), // NOI18N
1131: bundle.getString("CTL_EventsTabHint")) // NOI18N
1132: {
1133: public Node.Property[] getProperties() {
1134: return getEventProperties();
1135: }
1136: };
1137:
1138: ps.setValue("tabName", bundle.getString("CTL_EventsTab")); // NOI18N
1139: propSets.add(ps);
1140:
1141: }
1142:
1143: ps = new Node.PropertySet("synthetic", // NOI18N
1144: bundle.getString("CTL_SyntheticTab"), // NOI18N
1145: bundle.getString("CTL_SyntheticTabHint")) // NOI18N
1146: {
1147: public Node.Property[] getProperties() {
1148: return getSyntheticProperties();
1149: }
1150: };
1151: ps.setValue("tabName", bundle
1152: .getString("CTL_SyntheticTab_Short")); // NOI18N
1153: propSets.add(ps);
1154: }
1155:
1156: protected Node.Property[] createSyntheticProperties() {
1157: CodeGenerator codeGen = FormEditor.getCodeGenerator(formModel);
1158: return codeGen != null ? codeGen.getSyntheticProperties(this )
1159: : new Node.Property[0];
1160: }
1161:
1162: private void createBeanProperties() {
1163: List<RADProperty> prefProps = new ArrayList<RADProperty>();
1164: List<RADProperty> normalProps = new ArrayList<RADProperty>();
1165: List<RADProperty> expertProps = new ArrayList<RADProperty>();
1166: Map<Object, List<RADProperty>> otherProps = new TreeMap<Object, List<RADProperty>>();
1167: List<RADProperty> actionProps = new LinkedList<RADProperty>();
1168:
1169: Object[] propsCats = FormUtils.getPropertiesCategoryClsf(
1170: beanClass, getBeanInfo().getBeanDescriptor());
1171: Object[] propsAccess = FormUtils
1172: .getPropertiesAccessClsf(beanClass);
1173: Object[] propParentChildDepClsf = FormUtils
1174: .getPropertiesParentChildDepsClsf(beanClass);
1175:
1176: PropertyDescriptor[] props = getBeanInfo()
1177: .getPropertyDescriptors();
1178: for (int i = 0; i < props.length; i++) {
1179: PropertyDescriptor pd = props[i];
1180: boolean action = (pd.getValue("action") != null); // NOI18N
1181: Object category = pd.getValue("category"); // NOI18N
1182: List<RADProperty> listToAdd;
1183:
1184: if ((category == null) || (!(category instanceof String))) {
1185: Object propCat = FormUtils.getPropertyCategory(pd,
1186: propsCats);
1187: if (propCat == FormUtils.PROP_PREFERRED)
1188: listToAdd = prefProps;
1189: else if (propCat == FormUtils.PROP_NORMAL)
1190: listToAdd = normalProps;
1191: else if (propCat == FormUtils.PROP_EXPERT)
1192: listToAdd = expertProps;
1193: else
1194: continue; // PROP_HIDDEN
1195:
1196: } else {
1197: listToAdd = otherProps.get(category);
1198: if (listToAdd == null) {
1199: listToAdd = new ArrayList<RADProperty>();
1200: otherProps.put(category, listToAdd);
1201: }
1202: }
1203:
1204: RADProperty prop = getPropertyByName(pd.getName(),
1205: RADProperty.class, false);
1206: if (prop == null)
1207: prop = createBeanProperty(pd, propsAccess,
1208: propParentChildDepClsf);
1209:
1210: if (prop != null) {
1211: listToAdd.add(prop);
1212: if ("action".equals(pd.getName())
1213: && (listToAdd == prefProps)
1214: && javax.swing.Action.class.isAssignableFrom(pd
1215: .getPropertyType())) { // NOI18N
1216: action = true;
1217: prop.setValue("actionName", FormUtils
1218: .getBundleString("CTL_SetAction")); // NOI18N
1219: }
1220: if (action) {
1221: Object actionName = pd.getValue("actionName"); // NOI18N
1222: if (actionName instanceof String) {
1223: prop.setValue("actionName", actionName); // NOI18N
1224: }
1225: actionProps.add(prop);
1226: }
1227: }
1228: }
1229:
1230: changePropertiesExplicitly(prefProps, normalProps, expertProps);
1231:
1232: int prefCount = prefProps.size();
1233: int normalCount = normalProps.size();
1234: int expertCount = expertProps.size();
1235: int otherCount = 0;
1236:
1237: if (prefCount > 0) {
1238: beanProperties1 = new RADProperty[prefCount];
1239: prefProps.toArray(beanProperties1);
1240: if (normalCount + expertCount > 0) {
1241: normalProps.addAll(expertProps);
1242: beanProperties2 = new RADProperty[normalCount
1243: + expertCount];
1244: normalProps.toArray(beanProperties2);
1245: } else
1246: beanProperties2 = new RADProperty[0];
1247: } else {
1248: beanProperties1 = new RADProperty[normalCount];
1249: normalProps.toArray(beanProperties1);
1250: if (expertCount > 0) {
1251: beanProperties2 = new RADProperty[expertCount];
1252: expertProps.toArray(beanProperties2);
1253: } else
1254: beanProperties2 = new RADProperty[0];
1255: }
1256:
1257: Map<Object, RADProperty[]> otherProps2 = new TreeMap<Object, RADProperty[]>();
1258: RADProperty[] array = new RADProperty[0];
1259: for (Map.Entry<Object, List<RADProperty>> entry : otherProps
1260: .entrySet()) {
1261: List<RADProperty> catProps = entry.getValue();
1262: otherCount += catProps.size();
1263: otherProps2.put(entry.getKey(), catProps.toArray(array));
1264: }
1265: otherProperties = otherProps2;
1266:
1267: FormUtils.reorderProperties(beanClass, beanProperties1);
1268: FormUtils.reorderProperties(beanClass, beanProperties2);
1269:
1270: knownBeanProperties = new RADProperty[beanProperties1.length
1271: + beanProperties2.length + otherCount];
1272: System.arraycopy(beanProperties1, 0, knownBeanProperties, 0,
1273: beanProperties1.length);
1274: System.arraycopy(beanProperties2, 0, knownBeanProperties,
1275: beanProperties1.length, beanProperties2.length);
1276:
1277: int where = beanProperties1.length + beanProperties2.length;
1278:
1279: for (Map.Entry<Object, RADProperty[]> entry : otherProperties
1280: .entrySet()) {
1281: RADProperty[] catProps = entry.getValue();
1282: System.arraycopy(catProps, 0, knownBeanProperties, where,
1283: catProps.length);
1284: where += catProps.length;
1285: }
1286:
1287: actionProperties = actionProps;
1288: }
1289:
1290: private void createBindingProperties() {
1291: Collection<BindingDescriptor>[] props = FormEditor
1292: .getBindingSupport(formModel).getBindingDescriptors(
1293: this );
1294: bindingProperties = new BindingProperty[props.length][];
1295: for (int i = 0; i < props.length; i++) {
1296: bindingProperties[i] = new BindingProperty[props[i].size()];
1297: int j = 0;
1298: for (BindingDescriptor desc : props[i]) {
1299: bindingProperties[i][j++] = new BindingProperty(this ,
1300: desc);
1301: }
1302: }
1303: }
1304:
1305: private void createEventProperties() {
1306: EventSetDescriptor[] eventSets = getBeanInfo()
1307: .getEventSetDescriptors();
1308:
1309: List<EventProperty> eventPropList = new ArrayList<EventProperty>(
1310: eventSets.length * 5);
1311:
1312: for (int i = 0; i < eventSets.length; i++) {
1313: EventSetDescriptor desc = eventSets[i];
1314: Method[] methods = desc.getListenerMethods();
1315: for (int j = 0; j < methods.length; j++) {
1316: String eventId = FormEvents.getEventIdName(methods[j]);
1317: Object prop = nameToProperty.get(eventId);
1318: assert (prop == null)
1319: || (prop instanceof EventProperty);
1320: EventProperty eventProp = (EventProperty) prop;
1321: if (eventProp == null)
1322: eventProp = createEventProperty(eventId, desc,
1323: methods[j]);
1324: eventPropList.add(eventProp);
1325: }
1326: }
1327:
1328: EventProperty[] eventProps = new EventProperty[eventPropList
1329: .size()];
1330: eventPropList.toArray(eventProps);
1331:
1332: knownEvents = new Event[eventProps.length];
1333: for (int i = 0; i < eventProps.length; i++)
1334: knownEvents[i] = eventProps[i].getEvent();
1335:
1336: FormUtils.sortProperties(eventProps);
1337: eventProperties = eventProps;
1338: }
1339:
1340: protected RADProperty createBeanProperty(PropertyDescriptor desc,
1341: Object[] propAccessClsf, Object[] propParentChildDepClsf) {
1342: if (desc.getPropertyType() == null)
1343: return null;
1344:
1345: RADProperty prop = null;
1346: if (desc instanceof FakePropertyDescriptor) {
1347: try {
1348: prop = new FakeRADProperty(this ,
1349: (FakePropertyDescriptor) desc);
1350: } catch (IntrospectionException ex) { // should not happen
1351: ex.printStackTrace();
1352: return null;
1353: }
1354: } else {
1355: prop = new RADProperty(this , desc);
1356: }
1357:
1358: int access = FormUtils.getPropertyAccess(desc, propAccessClsf);
1359: if (access != 0)
1360: prop.setAccessType(access);
1361:
1362: String parentChildDependency = FormUtils
1363: .getPropertyParentChildDependency(desc,
1364: propParentChildDepClsf);
1365: if (parentChildDependency != null)
1366: prop.setValue(parentChildDependency, Boolean.TRUE);
1367:
1368: setPropertyListener(prop);
1369:
1370: // prop.addPropertyChangeListener(getPropertyListener());
1371: nameToProperty.put(desc.getName(), prop);
1372:
1373: return prop;
1374: }
1375:
1376: private EventProperty createEventProperty(String eventId,
1377: EventSetDescriptor eventDesc, Method eventMethod) {
1378: EventProperty prop = new EventProperty(new Event(this ,
1379: eventDesc, eventMethod), eventId);
1380: nameToProperty.put(eventId, prop);
1381: return prop;
1382: }
1383:
1384: /** Called to modify original bean properties obtained from BeanInfo.
1385: * Properties may be added, removed etc. - due to specific needs.
1386: *
1387: * @param prefProps preferred properties.
1388: * @param normalProps normal properties.
1389: * @param expertProps expert properties.
1390: */
1391: protected void changePropertiesExplicitly(
1392: List<RADProperty> prefProps, List<RADProperty> normalProps,
1393: List<RADProperty> expertProps) {
1394: // hack for buttons - add fake property for ButtonGroup
1395: if (getBeanInstance() instanceof javax.swing.AbstractButton) {
1396: try {
1397: RADProperty prop = new ButtonGroupProperty(this );
1398: setPropertyListener(prop);
1399: // prop.addPropertyChangeListener(getPropertyListener());
1400: nameToProperty.put(prop.getName(), prop);
1401:
1402: Object propCategory = FormUtils.getPropertyCategory(
1403: prop.getPropertyDescriptor(), FormUtils
1404: .getPropertiesCategoryClsf(beanClass,
1405: getBeanInfo()
1406: .getBeanDescriptor()));
1407:
1408: if (propCategory == FormUtils.PROP_PREFERRED)
1409: prefProps.add(prop);
1410: else
1411: normalProps.add(prop);
1412: } catch (IntrospectionException ex) {
1413: } // should not happen
1414: } else if (getBeanInstance() instanceof javax.swing.JTable) {
1415: try {
1416: PropertyDescriptor pd = new PropertyDescriptor(
1417: "rowHeight", javax.swing.JTable.class); // NOI18N
1418: RADProperty prop = createBeanProperty(pd, null, null);
1419: nameToProperty.put("rowHeight", prop); // NOI18N
1420: normalProps.add(prop);
1421: } catch (IntrospectionException ex) {
1422: ex.printStackTrace();
1423: }
1424: }
1425:
1426: // PENDING improve performance - keep lookup result, listen on it etc.
1427: boolean modified = false;
1428: Lookup.Template<PropertyModifier> template = new Lookup.Template<PropertyModifier>(
1429: PropertyModifier.class);
1430: Collection<? extends PropertyModifier> modifiers = Lookup
1431: .getDefault().lookup(template).allInstances();
1432: for (PropertyModifier modifier : modifiers) {
1433: modified |= modifier.modifyProperties(this , prefProps,
1434: normalProps, expertProps);
1435: }
1436:
1437: if (modified) {
1438: checkForAddedProperties(prefProps);
1439: checkForAddedProperties(normalProps);
1440: checkForAddedProperties(expertProps);
1441: }
1442: }
1443:
1444: private void checkForAddedProperties(List props) {
1445: for (Object o : props) {
1446: FormProperty prop = (FormProperty) o;
1447: String propName = prop.getName();
1448: if (!nameToProperty.containsKey(propName)) {
1449: nameToProperty.put(propName, prop);
1450: setPropertyListener(prop);
1451: }
1452: }
1453: }
1454:
1455: protected PropertyChangeListener createPropertyListener() {
1456: return new PropertyListenerConvertor();
1457: }
1458:
1459: public void setPropertyListener(FormProperty property) {
1460: if (propertyListener == null)
1461: propertyListener = createPropertyListener();
1462: if (propertyListener != null) {
1463: property.addPropertyChangeListener(propertyListener);
1464: if (propertyListener instanceof FormProperty.ValueConvertor)
1465: property
1466: .addValueConvertor((FormProperty.ValueConvertor) propertyListener);
1467: }
1468: }
1469:
1470: /** Listener class for listening to changes in component's properties.
1471: */
1472: private class PropertyListenerConvertor implements
1473: PropertyChangeListener, FormProperty.ValueConvertor {
1474: public void propertyChange(PropertyChangeEvent evt) {
1475: Object source = evt.getSource();
1476: if (!(source instanceof FormProperty))
1477: return;
1478:
1479: FormProperty property = (FormProperty) source;
1480: String propName = property.getName();
1481: String eventName = evt.getPropertyName();
1482:
1483: if (FormProperty.PROP_VALUE.equals(eventName)
1484: || FormProperty.PROP_VALUE_AND_EDITOR
1485: .equals(eventName)) { // property value has changed (or value and editor together)
1486: resourcePropertyChanged(evt);
1487:
1488: Object oldValue = evt.getOldValue();
1489: Object newValue = evt.getNewValue();
1490: formModel
1491: .fireComponentPropertyChanged(
1492: RADComponent.this , propName, oldValue,
1493: newValue);
1494:
1495: if (getNodeReference() != null) { // propagate the change to node
1496: if (FormProperty.PROP_VALUE_AND_EDITOR
1497: .equals(eventName)) {
1498: oldValue = ((FormProperty.ValueWithEditor) oldValue)
1499: .getValue();
1500: newValue = ((FormProperty.ValueWithEditor) newValue)
1501: .getValue();
1502: }
1503:
1504: getNodeReference().firePropertyChangeHelper(
1505: // null, null, null);
1506: propName, oldValue, newValue);
1507: }
1508: } else if (FormProperty.CURRENT_EDITOR.equals(eventName)) {
1509: // property editor has changed - don't fire to FormModel,
1510: // only to component node
1511: if (getNodeReference() != null)
1512: getNodeReference().firePropertyChangeHelper(
1513: propName, null, null);
1514: }
1515: }
1516:
1517: public Object convert(Object value, FormProperty property) {
1518: return resourcePropertyConvert(value, property);
1519: }
1520: }
1521:
1522: // -----
1523: // resources/i18n automation
1524:
1525: Object resourcePropertyConvert(Object value, FormProperty property) {
1526: if (isInModel() && formModel.isUndoRedoRecording()
1527: && property.getPropertyContext().getFormModel() != null) {
1528: Object resVal = ResourceSupport.makeResource(value,
1529: property);
1530: if (resVal != value)
1531: return resVal;
1532: }
1533: return value; // do nothing
1534: }
1535:
1536: void resourceComponentRename(String oldName, String newName) {
1537: if (isInModel()) {
1538: ResourceSupport.componentRenamed(this , oldName, newName);
1539: }
1540: }
1541:
1542: void resourcePropertyChanged(PropertyChangeEvent ev) {
1543: if (isInModel() && formModel.isFormLoaded()) {
1544: ResourceSupport.updateStoredValue(FormProperty
1545: .getEnclosedValue(ev.getOldValue()), // in case it is ValueWithEditor
1546: FormProperty.getEnclosedValue(ev.getNewValue()), // in case it is ValueWithEditor
1547: (FormProperty) ev.getSource());
1548: }
1549: }
1550:
1551: // ----------
1552:
1553: private static class PropertyIterator<T extends FormProperty>
1554: implements java.util.Iterator<T> {
1555: private T[] properties;
1556: private FormProperty.Filter filter;
1557:
1558: private T next;
1559: private int index;
1560:
1561: PropertyIterator(T[] properties, FormProperty.Filter filter) {
1562: this .properties = properties;
1563: this .filter = filter;
1564: }
1565:
1566: public boolean hasNext() {
1567: if (next == null)
1568: next = getNextProperty();
1569: return next != null;
1570: }
1571:
1572: public T next() {
1573: if (next == null)
1574: next = getNextProperty();
1575: if (next != null) {
1576: T prop = next;
1577: next = null;
1578: return prop;
1579: }
1580: throw new NoSuchElementException();
1581: }
1582:
1583: public void remove() {
1584: throw new UnsupportedOperationException();
1585: }
1586:
1587: private T getNextProperty() {
1588: while (index < properties.length) {
1589: T prop = properties[index++];
1590: if (filter.accept(prop))
1591: return prop;
1592: }
1593: return null;
1594: }
1595: }
1596:
1597: @Override
1598: public String toString() {
1599: return super .toString() + ", name: " + getName() + ", class: "
1600: + getBeanClass() + ", beaninfo: " + getBeanInfo()
1601: + ", instance: " + getBeanInstance(); // NOI18N
1602: }
1603:
1604: // ----------
1605: // a reference to a metacomponent - used instead of a metacomponent, may
1606: // become invalid when the component is removed
1607:
1608: interface ComponentReference {
1609: RADComponent getComponent();
1610: }
1611:
1612: // ------------------------------------
1613: // some hacks for ButtonGroup "component" ...
1614:
1615: // pseudo-property for buttons - holds ButtonGroup in which button
1616: // is placed; kind of "reversed" property
1617: static class ButtonGroupProperty extends RADProperty {
1618: ButtonGroupProperty(RADComponent comp)
1619: throws IntrospectionException {
1620: super (comp, new FakePropertyDescriptor("buttonGroup", // NOI18N
1621: javax.swing.ButtonGroup.class));
1622: setAccessType(DETACHED_READ | DETACHED_WRITE);
1623: setShortDescription(FormUtils
1624: .getBundleString("HINT_ButtonGroup")); // NOI18N
1625: }
1626:
1627: @Override
1628: public boolean supportsDefaultValue() {
1629: return true;
1630: }
1631:
1632: @Override
1633: public Object getDefaultValue() {
1634: return null;
1635: }
1636:
1637: @Override
1638: public PropertyEditor getExpliciteEditor() {
1639: return new ButtonGroupPropertyEditor();
1640: }
1641:
1642: @Override
1643: String getWholeSetterCode(String groupName) {
1644: return groupName != null ? groupName + ".add("
1645: + getRADComponent().getName() + ");" : // NOI18N
1646: null;
1647: }
1648: }
1649:
1650: // property editor for selecting ButtonGroup (for ButtonGroupProperty)
1651: public static class ButtonGroupPropertyEditor extends
1652: ComponentChooserEditor {
1653: public ButtonGroupPropertyEditor() {
1654: super ();
1655: setBeanTypes(new Class[] { javax.swing.ButtonGroup.class });
1656: setComponentCategory(NONVISUAL_COMPONENTS);
1657: }
1658:
1659: @Override
1660: public String getDisplayName() {
1661: return NbBundle.getBundle(getClass()).getString(
1662: "CTL_ButtonGroupPropertyEditor_DisplayName"); // NOI18N
1663: }
1664: }
1665:
1666: public void setValid(boolean valid) {
1667: this .valid = valid;
1668: }
1669:
1670: protected boolean isValid() {
1671: return valid;
1672: }
1673:
1674: private class FakeBeanInfo extends SimpleBeanInfo {
1675:
1676: private List<PropertyDescriptor> propertyDescriptors = new ArrayList<PropertyDescriptor>();
1677:
1678: @Override
1679: public BeanDescriptor getBeanDescriptor() {
1680: return (beanInfo == this ) ? new BeanDescriptor(beanClass)
1681: : beanInfo.getBeanDescriptor();
1682: }
1683:
1684: @Override
1685: public PropertyDescriptor[] getPropertyDescriptors() {
1686: return propertyDescriptors
1687: .toArray(new PropertyDescriptor[propertyDescriptors
1688: .size()]);
1689: }
1690:
1691: @Override
1692: public EventSetDescriptor[] getEventSetDescriptors() {
1693: return new EventSetDescriptor[0];
1694: }
1695:
1696: void addPropertyDescriptor(String propertyName,
1697: Class propertyClass) {
1698: try {
1699: propertyDescriptors.add(new FakePropertyDescriptor(
1700: propertyName, propertyClass));
1701: } catch (IntrospectionException ex) {
1702: ex.printStackTrace(); // should not happen
1703: }
1704: }
1705:
1706: void addPropertyDescriptor(PropertyDescriptor pd) {
1707: propertyDescriptors.add(pd);
1708: }
1709:
1710: void removePropertyDescriptors() {
1711: propertyDescriptors.clear();
1712: }
1713: }
1714: }
|