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.lang.reflect.*;
0046: import org.openide.nodes.Node;
0047:
0048: /**
0049: * This class provides basic implementation of properties used in form module
0050: * which are generated in the java code. FormProperty can use multiple property
0051: * editors (via FormPropertyEditor) and special "design values" (holding some
0052: * additional data - FormDesignValue implementations).
0053: *
0054: * FormProperty is an "interface" object that provides general access to one
0055: * property of some other object (called "target object"). To make it work,
0056: * only some connection to the target object must be implemented. There are
0057: * two (abstract) methods for this purpose in FormProperty class:
0058: * public Object getTargetValue();
0059: * public void setTargetValue(Object value);
0060: *
0061: * NOTE: Binding to target object can be switched off for reading or writing
0062: * by setting access type of property to DETACHED_READ or DETACHED_WRITE.
0063: *
0064: * There are some further methods (potentially suitable) for custom
0065: * implementation (overriding the default implementation):
0066: * public boolean supportsDefaultValue();
0067: * public Object getDefaultValue();
0068: * public PropertyEditor getExpliciteEditor();
0069: *
0070: * NOTE: Properties are created for nodes and presented in property sheet.
0071: * Node object that owns properties should listen to the CURRENT_EDITOR
0072: * property change on each property and call firePropertySetsChange(...)
0073: * to notify the sheet about changing current property editor of a property.
0074: *
0075: * @author Tomas Pavek
0076: */
0077:
0078: public abstract class FormProperty extends Node.Property {
0079:
0080: // --------------------
0081: // constants
0082:
0083: public static final String PROP_VALUE = "propertyValue"; // NOI18N
0084: public static final String CURRENT_EDITOR = "currentEditor"; // NOI18N
0085: public static final String PROP_VALUE_AND_EDITOR = "propertyValueAndEditor"; // NOI18N
0086: public static final String PROP_PRE_CODE = "preCode"; // NOI18N
0087: public static final String PROP_POST_CODE = "postCode"; // NOI18N
0088:
0089: // "Access type" of the property (in relation to the target object).
0090: // There are three levels of restriction possible:
0091: // NORMAL_RW - no restriction on both property and target object
0092: // DETACHED_READ, DETACHED_WRITE - no reading or writing on the target
0093: // object (it is "detached"; the value is cached by the property)
0094: // NO_READ, NO_WRITE - it is not possible to perform read or write on
0095: // the property (so neither on the target object)
0096: public static final int NORMAL_RW = 0;
0097:
0098: public static final int DETACHED_READ = 1; // no reading from target (bit 0)
0099: public static final int DETACHED_WRITE = 2; // no writing to target (bit 1)
0100:
0101: private static final int NO_READ_PROP = 4; // bit 2
0102: private static final int NO_WRITE_PROP = 8; // bit 3
0103: public static final int NO_READ = DETACHED_READ | NO_READ_PROP; // no reading from property (bits 0,2)
0104: public static final int NO_WRITE = DETACHED_WRITE | NO_WRITE_PROP; // no writing to property (bits 1,3)
0105:
0106: // ------------------------
0107: // variables
0108: protected int propType = NORMAL_RW;
0109:
0110: FormPropertyContext propertyContext;
0111:
0112: protected Object propertyValue; // cached value of the property
0113: protected boolean valueSet = false; // propertyValue validity
0114: boolean valueChanged = false; // non-default value that came in through setValue
0115:
0116: private boolean externalChangeMonitoring = true;
0117: private Object lastRealValue; // for detecting external change of the property value
0118:
0119: String preCode;
0120: String postCode;
0121:
0122: FormPropertyEditor formPropertyEditor;
0123: PropertyEditor currentEditor;
0124:
0125: private PropertyChangeSupport changeSupport;
0126: private VetoableChangeSupport vetoableChangeSupport;
0127: private boolean fireChanges = true;
0128:
0129: private java.util.List<ValueConvertor> convertors;
0130:
0131: // private DesignValueListener designValueListener = null;
0132:
0133: // ---------------------------
0134: // constructors
0135:
0136: protected FormProperty(FormPropertyContext propertyContext,
0137: String name, Class type, String displayName,
0138: String shortDescription) {
0139: super (type);
0140: setValue("changeImmediate", Boolean.FALSE); // NOI18N
0141: setName(name);
0142: setDisplayName(displayName);
0143: setShortDescription(getDescriptionWithType(shortDescription));
0144:
0145: this .propertyContext = FormPropertyContext.EmptyImpl
0146: .getInstance();
0147: setPropertyContext(propertyContext);
0148: }
0149:
0150: protected FormProperty(FormPropertyContext propertyContext,
0151: Class type) {
0152: super (type);
0153: setValue("changeImmediate", Boolean.FALSE); // NOI18N
0154:
0155: this .propertyContext = FormPropertyContext.EmptyImpl
0156: .getInstance();
0157: setPropertyContext(propertyContext);
0158: }
0159:
0160: // constructor of property without PropertyContext
0161: // setPropertyContext(...) should be called explicitly then
0162: protected FormProperty(String name, Class type, String displayName,
0163: String shortDescription) {
0164: super (type);
0165: setValue("changeImmediate", Boolean.FALSE); // NOI18N
0166: setName(name);
0167: setDisplayName(displayName);
0168: setShortDescription(getDescriptionWithType(shortDescription));
0169:
0170: this .propertyContext = FormPropertyContext.EmptyImpl
0171: .getInstance();
0172: }
0173:
0174: // constructor of property without PropertyContext;
0175: // setPropertyContext(...) must be called explicitly before the property
0176: // is used first time
0177: protected FormProperty(Class type) {
0178: super (type);
0179: setValue("changeImmediate", Boolean.FALSE); // NOI18N
0180:
0181: this .propertyContext = FormPropertyContext.EmptyImpl
0182: .getInstance();
0183: }
0184:
0185: private String getDescriptionWithType(String description) {
0186: String type = org.openide.util.Utilities
0187: .getClassName(getValueType());
0188: return description == null ? FormUtils
0189: .getFormattedBundleString("HINT_PropertyType", // NOI18N
0190: new Object[] { type }) : FormUtils
0191: .getFormattedBundleString(
0192: "HINT_PropertyTypeWithDescription", // NOI18N
0193: new Object[] { type, description });
0194: }
0195:
0196: // ----------------------------------------
0197: // getter, setter & related methods
0198:
0199: @Override
0200: public String getHtmlDisplayName() {
0201: if (isChanged()) {
0202: return "<b>" + getDisplayName(); // NOI18N
0203: } else {
0204: return null;
0205: }
0206: }
0207:
0208: /** Gets the real value of this property directly from the target object.
0209: *
0210: * @return real value value of this property directly from the target object.
0211: * @throws java.lang.IllegalAccessException when there is an access problem.
0212: * @throws java.lang.reflect.InvocationTargetException when there is an invocation problem.
0213: */
0214: public abstract Object getTargetValue()
0215: throws IllegalAccessException, InvocationTargetException;
0216:
0217: /** Sets the real property value directly to the target object.
0218: *
0219: * @param value
0220: * @throws java.lang.IllegalAccessException when there is an access problem.
0221: * @throws java.lang.IllegalArgumentException when the specified value is not valid.
0222: * @throws java.lang.reflect.InvocationTargetException when there is an invocation problem.
0223: */
0224: public abstract void setTargetValue(Object value)
0225: throws IllegalAccessException, IllegalArgumentException,
0226: InvocationTargetException;
0227:
0228: private void setTargetValueInLAFBlock(final Object value)
0229: throws IllegalAccessException, IllegalArgumentException,
0230: InvocationTargetException {
0231: if (!FormLAF.inLAFBlock()
0232: && (propertyContext.getFormModel() != null)) {
0233: final Exception[] ex = new Exception[1];
0234: FormLAF.executeWithLookAndFeel(propertyContext
0235: .getFormModel(), new Runnable() {
0236: public void run() {
0237: try {
0238: setTargetValue(value);
0239: } catch (IllegalAccessException iaex) {
0240: ex[0] = iaex;
0241: } catch (IllegalArgumentException argex) {
0242: ex[0] = argex;
0243: } catch (InvocationTargetException itex) {
0244: ex[0] = itex;
0245: }
0246: }
0247: });
0248: if (ex[0] != null) {
0249: if (ex[0] instanceof IllegalArgumentException) {
0250: throw new IllegalArgumentException(ex[0]);
0251: } else if (ex[0] instanceof InvocationTargetException) {
0252: throw new InvocationTargetException(ex[0]);
0253: } else if (ex[0] instanceof IllegalAccessException) {
0254: throw (IllegalAccessException) ex[0];
0255: }
0256: }
0257: } else {
0258: setTargetValue(value);
0259: }
0260: }
0261:
0262: /** Gets the value of the property.
0263: *
0264: * @throws java.lang.IllegalAccessException when there is an access problem.
0265: * @throws java.lang.reflect.InvocationTargetException when there is an invocation problem.
0266: */
0267: public Object getValue() throws IllegalAccessException,
0268: InvocationTargetException {
0269: // if (!canRead())
0270: // throw new IllegalAccessException("Not a readable property: "+getName());
0271: Object value = checkCurrentValue();
0272:
0273: if (valueSet || (propType & DETACHED_READ) == 0)
0274: return value;
0275:
0276: return getDefaultValue();
0277: }
0278:
0279: /** Sets the property value.
0280: *
0281: * @param value property value.
0282: * @throws java.lang.IllegalAccessException when there is an access problem.
0283: * @throws java.lang.IllegalArgumentException when the specified value is not valid.
0284: * @throws java.lang.reflect.InvocationTargetException when there is an invocation problem.
0285: */
0286: public void setValue(Object value) throws IllegalAccessException,
0287: IllegalArgumentException, InvocationTargetException {
0288: // if (!canWrite())
0289: // throw new IllegalAccessException("Not a writeable property: "+getName());
0290: // let the registered converters do something with the value (e.g. i18n)
0291: if (fireChanges)
0292: value = convertValue(value);
0293:
0294: Object oldValue;
0295: if (canRead()) {
0296: try { // get the old value (still the current)
0297: oldValue = getValue();
0298: } catch (Exception e) { // no problem -> keep null
0299: oldValue = BeanSupport.NO_VALUE;
0300: }
0301: } else
0302: oldValue = BeanSupport.NO_VALUE;
0303:
0304: if (value instanceof ValueWithEditor) {
0305: // changing value and property editor at once
0306: ValueWithEditor vwpe = (ValueWithEditor) value;
0307: value = vwpe.getValue();
0308: PropertyEditor newEditor = vwpe.getPropertyEditor(this );
0309: PropertyEditor oldEditor = currentEditor;
0310:
0311: if (newEditor != oldEditor) {
0312: // turn off change firing as we fire the two changes as one
0313: boolean fire = fireChanges;
0314: fireChanges = false;
0315: setCurrentEditor(newEditor);
0316: setValue(value);
0317: fireChanges = fire;
0318:
0319: if (oldValue == BeanSupport.NO_VALUE)
0320: oldValue = null; // [should not BeanSupport.NO_VALUE remain??]
0321:
0322: propertyValueAndEditorChanged(new ValueWithEditor(
0323: oldValue, oldEditor), new ValueWithEditor(
0324: value, newEditor));
0325:
0326: return;
0327: }
0328: // othrewise continue setting only the value itself
0329: }
0330:
0331: if (oldValue != BeanSupport.NO_VALUE) {
0332: // check whether the new value is different
0333: if (!(value instanceof FormDesignValue)
0334: && equals(value, oldValue))
0335: return; // no change
0336: } else
0337: oldValue = null; // [should not BeanSupport.NO_VALUE remain??]
0338:
0339: if (value == BeanSupport.NO_VALUE) {
0340: // special value to be set - reset the change flag
0341: valueSet = false;
0342: setChanged(false);
0343: propertyValue = value;
0344: lastRealValue = null;
0345: propertyValueChanged(oldValue, value);
0346: return;
0347: }
0348:
0349: Object defValue = supportsDefaultValue() ? getDefaultValue()
0350: : BeanSupport.NO_VALUE;
0351:
0352: if (canWriteToTarget()) {
0353: // derive real value
0354: Object realValue = getRealValue(value);
0355:
0356: // set the real value to the target object
0357: if (realValue != FormDesignValue.IGNORED_VALUE) {
0358: setTargetValueInLAFBlock(realValue);
0359: } else if (valueSet && defValue != BeanSupport.NO_VALUE) {
0360: setTargetValueInLAFBlock(defValue);
0361: }
0362:
0363: if (canReadFromTarget()) {
0364: lastRealValue = getTargetValue();
0365: // if (value == realValue)
0366: // value = lastRealValue;
0367:
0368: /*
0369: Some bad properties of bad beans return another value than the one just set.
0370: So which one should be then used as the valid property value (displayed,
0371: generated in code, etc) - the one just set, or that got in turn from getter?
0372: (1) When the value just set is taken, then e.g. NONE_OPTION (-1) set to
0373: debugGraphicsOption of JComponent will be used and code generated, altough it
0374: is converted to 0 which is the default value, so no code should be generated.
0375: (2) When oppositely the value from getter after performing setter is taken,
0376: then e.g. setting "text/xml" to contentType of JEditorPane may fail at design
0377: time (editor kit is not found), so the value reverts to "text/plain" (default)
0378: and no code is generated, however it could work at runtime, so the code
0379: should be generated.
0380: [See also bug 12413.]
0381: */
0382: }
0383: }
0384:
0385: propertyValue = value; // cache the value for later...
0386: valueSet = true;
0387:
0388: // "changed" == property is readable and writeable and the new value
0389: // is not equal to the default value (or default value doesn't exist).
0390: setChanged((propType & (NO_READ_PROP | NO_WRITE_PROP)) == 0
0391: && (defValue == BeanSupport.NO_VALUE || !equals(value,
0392: defValue)));
0393:
0394: // settleDesignValueListener(oldValue, value);
0395: propertyValueChanged(oldValue, value);
0396: }
0397:
0398: /** This method gets the real value of the property. This is a support
0399: * for special "design values" that hold additional information besides
0400: * the real value that can be set directly to target object.
0401: *
0402: * @return real value of the property.
0403: * @throws java.lang.IllegalAccessException when there is an access problem.
0404: * @throws java.lang.reflect.InvocationTargetException when there is an invocation problem.
0405: */
0406: public final Object getRealValue() throws IllegalAccessException,
0407: InvocationTargetException {
0408: return getRealValue(getValue());
0409: }
0410:
0411: /** This method "extracts" the real value from the given object.
0412: * FormDesignValue is recognized by default. Subclasses may override
0413: * this method to provide additional conversions.
0414: *
0415: * @param value value that should be possibly extracted.
0416: * @return real value.
0417: */
0418: protected Object getRealValue(Object value) {
0419: while (value instanceof FormDesignValue) {
0420: Object prev = value;
0421: value = ((FormDesignValue) value).getDesignValue();
0422: if (value == prev)
0423: break;
0424: }
0425: return value;
0426: }
0427:
0428: /** Returns whether this property has a default value (false by default).
0429: * If any subclass provides default value, it should override this
0430: * and getDefaultValue() methods.
0431: * @return true if there is a default value, false otherwise
0432: */
0433: @Override
0434: public boolean supportsDefaultValue() {
0435: return false;
0436: }
0437:
0438: @Override
0439: public boolean isDefaultValue() {
0440: return supportsDefaultValue() ? !isChanged() : true;
0441: }
0442:
0443: /** Returns a default value of this property.
0444: * If any subclass provides default value, it should override this
0445: * and supportsDefaultValue() methods.
0446: * @return the default value (null by default :)
0447: */
0448: public Object getDefaultValue() {
0449: return null;
0450: }
0451:
0452: /** Restores the property to its default value.
0453: *
0454: * @throws java.lang.IllegalAccessException when there is an access problem.
0455: * @throws java.lang.reflect.InvocationTargetException when there is an invocation problem.
0456: */
0457: @Override
0458: public void restoreDefaultValue() throws IllegalAccessException,
0459: InvocationTargetException {
0460: // if (!canWrite()) return;
0461: setChanged(false);
0462:
0463: Object oldValue = null;
0464: Object defValue = getDefaultValue();
0465:
0466: if (canRead()) {
0467: try { // get the old value (still the current)
0468: oldValue = getValue();
0469: if (!(defValue instanceof FormDesignValue)
0470: && equals(defValue, oldValue))
0471: return; // no change
0472: } catch (Exception e) {
0473: } // no problem -> keep null
0474: }
0475:
0476: if (canWriteToTarget()) {
0477: // derive real value (from the default value)
0478: Object realValue = getRealValue(defValue);
0479:
0480: try {
0481: // set the default real value to the target
0482: if (realValue != FormDesignValue.IGNORED_VALUE) {
0483: setTargetValueInLAFBlock(realValue);
0484: // lastRealValue = realValue;
0485: } else if (defValue != BeanSupport.NO_VALUE) {
0486: setTargetValueInLAFBlock(defValue);
0487: // lastRealValue = defValue;
0488: }
0489: // else if (isExternalChangeMonitoring())
0490: // lastRealValue = getTargetValue();
0491:
0492: lastRealValue = getTargetValue();
0493: } catch (IllegalArgumentException e) {
0494: } // should not happen
0495: }
0496:
0497: propertyValue = defValue;
0498: valueSet = true;
0499:
0500: // set default property editor as current
0501: PropertyEditor prEd = findDefaultEditor();
0502: if (prEd != null)
0503: setCurrentEditor(prEd);
0504:
0505: // settleDesignValueListener(oldValue, defValue);
0506: propertyValueChanged(oldValue, defValue);
0507: }
0508:
0509: /** This method re-sets cached value of the property to the target object.
0510: * (If there is no cached value here, nothing is set to target object.)
0511: * This may be useful when target object was re-created and needs to be
0512: * initialized in accordance with current properties.
0513: *
0514: * @throws java.lang.IllegalAccessException when there is an access problem.
0515: * @throws java.lang.reflect.InvocationTargetException when there is an invocation problem.
0516: */
0517: public void reinstateTarget() throws IllegalAccessException,
0518: InvocationTargetException {
0519: if (valueSet && canWriteToTarget())
0520: try {
0521: // re-set the real value of the property of the target object
0522: Object realValue = getRealValue(propertyValue);
0523:
0524: if (realValue != FormDesignValue.IGNORED_VALUE) {
0525: setTargetValueInLAFBlock(realValue);
0526: lastRealValue = realValue;
0527: } else if (isExternalChangeMonitoring())
0528: lastRealValue = getTargetValue();
0529: } catch (IllegalArgumentException e) { // should not happen
0530: }
0531: }
0532:
0533: /** This method updates state of the property according to the target
0534: * object. This may be useful when property needs to be initialized
0535: * with existing target object. But this approach doesn't work well with
0536: * bound and derived properties...
0537: *
0538: * @throws java.lang.IllegalAccessException when there is an access problem.
0539: * @throws java.lang.reflect.InvocationTargetException when there is an invocation problem.
0540: */
0541: public void reinstateProperty() throws IllegalAccessException,
0542: InvocationTargetException {
0543: boolean mayChanged = canReadFromTarget()
0544: && (propType & (NO_READ_PROP | NO_WRITE_PROP)) == 0;
0545:
0546: if (mayChanged) {
0547: Object value = getTargetValue();
0548: if (supportsDefaultValue()) {
0549: Object defValue = getDefaultValue();
0550: mayChanged = !equals(value, defValue);
0551: }
0552: if (mayChanged) {
0553: propertyValue = value;
0554: lastRealValue = value;
0555: }
0556: }
0557:
0558: valueSet = mayChanged;
0559: setChanged(mayChanged);
0560: }
0561:
0562: // ------------------------------
0563: // boolean flags
0564:
0565: /** Tests whether the property is readable.
0566: */
0567: public boolean canRead() {
0568: return (propType & NO_READ_PROP) == 0;
0569: }
0570:
0571: /** Tests whether the property is writable.
0572: */
0573: public boolean canWrite() {
0574: return (propType & NO_WRITE_PROP) == 0;
0575: }
0576:
0577: public final boolean canReadFromTarget() {
0578: return /*canRead() &&*/(propType & DETACHED_READ) == 0;
0579: }
0580:
0581: public final boolean canWriteToTarget() {
0582: return /*canWrite() &&*/(propType & DETACHED_WRITE) == 0;
0583: }
0584:
0585: /** Tests whether this property is marked as "changed". This method returns
0586: * true if the value of the property is different from the default value
0587: * and if it is accessible and replicable (readable and writeable property).
0588: *
0589: * @return <code>true</code> if the property was changed,
0590: * returns <code>false</code> otherwise.
0591: */
0592: public boolean isChanged() {
0593: if (valueChanged && valueSet) { // update the changed flag
0594: try {
0595: checkCurrentValue();
0596: } catch (Exception ex) {
0597: }
0598: }
0599: return valueChanged;
0600: }
0601:
0602: /** Sets explicitly the flag indicating changed property.
0603: *
0604: * @param changed determines whether this property was changed.
0605: */
0606: public void setChanged(boolean changed) {
0607: valueChanged = changed;
0608: }
0609:
0610: // --------------------------------
0611: // property editors
0612:
0613: /** Gets a property editor for this property. This method implements
0614: * Node.Property.getPropertyEditor() and need not be further overriden.
0615: * It enables using of multiple individual editors by constructing
0616: * FormPropertyEditor class. There are other methods for controling the
0617: * FormPropertyEditor class here - see: getCurrentEditor(),
0618: * setCurrentEditor(...) and getExpliciteEditor().
0619: */
0620: @Override
0621: public PropertyEditor getPropertyEditor() {
0622: PropertyEditor prEd;
0623:
0624: if (formPropertyEditor == null) {
0625: if (propertyContext.useMultipleEditors()) {
0626: formPropertyEditor = new FormPropertyEditor(this );
0627: prEd = formPropertyEditor;
0628: } else
0629: prEd = getCurrentEditor();
0630: } else
0631: prEd = formPropertyEditor;
0632:
0633: return prEd;
0634: }
0635:
0636: /** Gets the currently selected property editor (from multiple editors
0637: * managed by FormPropertyEditor).
0638: *
0639: * @return current property editor.
0640: */
0641: public final PropertyEditor getCurrentEditor() {
0642: if (currentEditor == null) {
0643: currentEditor = findDefaultEditor();
0644: if (currentEditor != null)
0645: propertyContext.initPropertyEditor(currentEditor, this );
0646: }
0647: return currentEditor;
0648: }
0649:
0650: /** Sets the current property editor that will be used for this property
0651: * by FormPropertyEditor.
0652: *
0653: * @param newEditor current property editor.
0654: */
0655: public final void setCurrentEditor(PropertyEditor newEditor) {
0656: if (newEditor != currentEditor) {
0657: if (newEditor != null)
0658: propertyContext.initPropertyEditor(newEditor, this );
0659:
0660: if (formPropertyEditor != null) {
0661: if (currentEditor != null)
0662: currentEditor
0663: .removePropertyChangeListener(formPropertyEditor);
0664: if (newEditor != null)
0665: newEditor
0666: .addPropertyChangeListener(formPropertyEditor);
0667: }
0668:
0669: PropertyEditor old = currentEditor;
0670: currentEditor = newEditor;
0671: currentEditorChanged(old, newEditor);
0672: }
0673: }
0674:
0675: /** Gets the property editor explicitly designated for this property.
0676: * This editor is taken as default by FormPropertyEditor.
0677: * Subclasses should override this method if they provide a special
0678: * editor for this property.
0679: *
0680: * @return property editor explicitly designated for this property.
0681: */
0682: public PropertyEditor getExpliciteEditor() {
0683: return null;
0684: }
0685:
0686: // ------------------------------
0687: // code generation
0688:
0689: /** Gets the java code initializing the property value. It is obtained from
0690: * current property editor. Example: "Button 1"
0691: *
0692: * @return initialization string.
0693: */
0694: public String getJavaInitializationString() {
0695: try {
0696: Object value = getValue();
0697: if (value == null)
0698: return "null"; // NOI18N
0699:
0700: if (value == BeanSupport.NO_VALUE)
0701: return null;
0702:
0703: PropertyEditor ed = getCurrentEditor();
0704: if (ed == null)
0705: return null;
0706:
0707: // should we create a new instance of editor?
0708: // if (ed instanceof RADConnectionPropertyEditor)
0709: // ed = new RADConnectionPropertyEditor(getValueType());
0710: // else
0711: // ed = (PropertyEditor)ed.getClass().newInstance();
0712: // propertyContext.initPropertyEditor(ed);
0713:
0714: if (ed.getValue() != value)
0715: ed.setValue(value);
0716: return ed.getJavaInitializationString();
0717: } catch (Exception e) {
0718: e.printStackTrace();
0719: }
0720: return null;
0721: }
0722:
0723: /** Gets the java code for setting the property value (without the object
0724: * on which the property is set, and without semicolon at the end).
0725: * This method is optional. Example: setText("Button 1")
0726: */
0727: String getPartialSetterCode(String javaInitStr) {
0728: if (javaInitStr == null)
0729: return null;
0730:
0731: Method writeMethod = getWriteMethod();
0732: if (writeMethod == null)
0733: return null;
0734:
0735: return writeMethod.getName() + "(" + javaInitStr + ")"; // NOI18N
0736: }
0737:
0738: /** Gets the complete java code for setting the property, including the
0739: * semicolon at the end of the line. This method is optional.
0740: * Example: jButton1.setText("Button 1");
0741: */
0742: String getWholeSetterCode(String javaInitStr) {
0743: return null;
0744: }
0745:
0746: /**
0747: * Gets the write method setting the property.
0748: * Used by {@link JavaCodeGenerator}.
0749: *
0750: * @return write method.
0751: */
0752: protected Method getWriteMethod() {
0753: return null;
0754: }
0755:
0756: /** Gets the code to be generated before the property setter code
0757: * (on separate line).
0758: *
0759: * @return pre-initialization code.
0760: */
0761: public String getPreCode() {
0762: return preCode;
0763: }
0764:
0765: /** Gets the code to be generated after the property setter code
0766: * (on separate line).
0767: *
0768: * @return post-initialization code.
0769: */
0770: public String getPostCode() {
0771: return postCode;
0772: }
0773:
0774: /** Sets the code to be generated before the property setter code
0775: * (on separate line).
0776: *
0777: * @param value pre-initialization code.
0778: */
0779: public void setPreCode(String value) {
0780: preCode = value;
0781: }
0782:
0783: /** Sets the code to be generated after the property setter code
0784: * (on separate line).
0785: *
0786: * @param value post-initialization code.
0787: */
0788: public void setPostCode(String value) {
0789: postCode = value;
0790: }
0791:
0792: // ------------------------
0793:
0794: public FormPropertyContext getPropertyContext() {
0795: return propertyContext;
0796: }
0797:
0798: public void setPropertyContext(FormPropertyContext newContext) {
0799: if (newContext == null)
0800: newContext = FormPropertyContext.EmptyImpl.getInstance();
0801: if (propertyContext != null
0802: && formPropertyEditor != null
0803: && propertyContext.useMultipleEditors() != newContext
0804: .useMultipleEditors()) {
0805: if (currentEditor != null)
0806: currentEditor
0807: .removePropertyChangeListener(formPropertyEditor);
0808: formPropertyEditor = null;
0809: }
0810:
0811: propertyContext = newContext;
0812:
0813: if (currentEditor != null)
0814: propertyContext.initPropertyEditor(currentEditor, this );
0815: }
0816:
0817: public int getAccessType() {
0818: return propType;
0819: }
0820:
0821: public void setAccessType(int type) {
0822: if (type >= 0)
0823: propType = type;
0824: }
0825:
0826: public boolean isExternalChangeMonitoring() {
0827: return externalChangeMonitoring && propType == NORMAL_RW;
0828: }
0829:
0830: public void setExternalChangeMonitoring(boolean val) {
0831: externalChangeMonitoring = val;
0832: }
0833:
0834: // ----------------------------
0835:
0836: public void addPropertyChangeListener(PropertyChangeListener l) {
0837: synchronized (this ) {
0838: if (changeSupport == null)
0839: changeSupport = new PropertyChangeSupport(this );
0840: }
0841: changeSupport.addPropertyChangeListener(l);
0842: }
0843:
0844: public void removePropertyChangeListener(PropertyChangeListener l) {
0845: if (changeSupport != null)
0846: changeSupport.removePropertyChangeListener(l);
0847: }
0848:
0849: public void addVetoableChangeListener(VetoableChangeListener l) {
0850: synchronized (this ) {
0851: if (vetoableChangeSupport == null)
0852: vetoableChangeSupport = new VetoableChangeSupport(this );
0853: }
0854: vetoableChangeSupport.addVetoableChangeListener(l);
0855: }
0856:
0857: public void removeVetoableChangeListener(VetoableChangeListener l) {
0858: if (vetoableChangeSupport != null)
0859: vetoableChangeSupport.removeVetoableChangeListener(l);
0860: }
0861:
0862: public boolean isChangeFiring() {
0863: return fireChanges;
0864: }
0865:
0866: public void setChangeFiring(boolean fire) {
0867: fireChanges = fire;
0868: }
0869:
0870: protected void propertyValueChanged(Object old, Object current) {
0871: if (fireChanges) {
0872: try {
0873: firePropertyChange(PROP_VALUE, old, current);
0874:
0875: // evaluate the required form version level for this value
0876: Object value;
0877: PropertyEditor editor;
0878: if (current instanceof ValueWithEditor) {
0879: editor = ((ValueWithEditor) current)
0880: .getPropertyEditor();
0881: value = ((ValueWithEditor) current).getValue();
0882: } else {
0883: value = current;
0884: editor = currentEditor;
0885: }
0886: FormUtils.checkVersionLevelForProperty(this , value,
0887: editor);
0888: } catch (PropertyVetoException ex) {
0889: boolean fire = fireChanges;
0890: fireChanges = false;
0891: try {
0892: setValue(old);
0893: } catch (Exception ex2) {
0894: } // ignore
0895: fireChanges = fire;
0896: }
0897: }
0898: }
0899:
0900: protected void currentEditorChanged(PropertyEditor old,
0901: PropertyEditor current) {
0902: if (fireChanges) {
0903: try {
0904: firePropertyChange(CURRENT_EDITOR, old, current);
0905: } catch (PropertyVetoException ex) {
0906: } // won't happen
0907: }
0908: }
0909:
0910: protected void propertyValueAndEditorChanged(ValueWithEditor old,
0911: ValueWithEditor current) {
0912: if (fireChanges) {
0913: try {
0914: firePropertyChange(PROP_VALUE_AND_EDITOR, old, current);
0915: } catch (PropertyVetoException ex) {
0916: boolean fire = fireChanges;
0917: fireChanges = false;
0918: try {
0919: setValue(old);
0920: } catch (Exception ex2) {
0921: } // ignore
0922: fireChanges = fire;
0923: }
0924: }
0925: }
0926:
0927: private void firePropertyChange(String propName, Object old,
0928: Object current) throws PropertyVetoException {
0929: if (vetoableChangeSupport != null
0930: && !CURRENT_EDITOR.equals(propName)) {
0931: vetoableChangeSupport.fireVetoableChange(propName, old,
0932: current);
0933: }
0934: if (changeSupport != null) {
0935: changeSupport.firePropertyChange(propName, old, current);
0936: }
0937: }
0938:
0939: public void addValueConvertor(ValueConvertor conv) {
0940: synchronized (this ) {
0941: if (convertors == null)
0942: convertors = new java.util.LinkedList<ValueConvertor>();
0943: else
0944: convertors.remove(conv);
0945: convertors.add(conv);
0946: }
0947: }
0948:
0949: public void removeValueConvertor(ValueConvertor conv) {
0950: synchronized (this ) {
0951: if (convertors != null)
0952: convertors.remove(conv);
0953: }
0954: }
0955:
0956: protected Object convertValue(Object value) {
0957: if (convertors != null) {
0958: for (ValueConvertor conv : convertors) {
0959: Object val = conv.convert(value, this );
0960: if (val != value)
0961: return val;
0962: }
0963: }
0964: return value;
0965: }
0966:
0967: // ----------------------------
0968: // private methods
0969:
0970: private Object checkCurrentValue() throws IllegalAccessException,
0971: InvocationTargetException {
0972: if (valueSet) {
0973: Object value = null;
0974:
0975: if (isExternalChangeMonitoring()) {
0976: value = getTargetValue();
0977: if (!equals(value, lastRealValue)) {
0978: // the value is different from the one last set
0979: Object propValue = (propertyValue instanceof FormDesignValue) ? ((FormDesignValue) propertyValue)
0980: .getDesignValue()
0981: : propertyValue;
0982: if (propValue != FormDesignValue.IGNORED_VALUE) {
0983: // TODO check type of the value, beware of boolean != Boolean
0984: // assert (propValue == null) || getValueType().isAssignableFrom(propValue.getClass());
0985: valueSet = false;
0986: setChanged(false);
0987: lastRealValue = null;
0988: return value;
0989: // [fire property editor change - for refreshing property sheet??]
0990: }
0991: }
0992: }
0993: return propertyValue;
0994: }
0995: return (propType & DETACHED_READ) == 0 ? getTargetValue()
0996: : null;
0997: }
0998:
0999: PropertyEditor findDefaultEditor() {
1000: PropertyEditor defaultEditor = getExpliciteEditor();
1001: if (defaultEditor != null)
1002: return defaultEditor;
1003: return FormPropertyEditorManager.findEditor(this );
1004: }
1005:
1006: // --------
1007:
1008: // [we could probably use org.openide.util.Utilities.compareObjects instead]
1009: private static boolean equals(Object obj1, Object obj2) {
1010: if (obj1 == obj2)
1011: return true;
1012:
1013: if (obj1 == null || obj2 == null)
1014: return false;
1015:
1016: Class cls1 = obj1.getClass();
1017: Class cls2 = obj2.getClass();
1018:
1019: if (!cls1.isArray() || !cls1.equals(cls2))
1020: return obj1.equals(obj2);
1021:
1022: // and this is what is special on this method - comparing arrays...
1023: Class cType = cls1.getComponentType();
1024: if (!cType.isPrimitive()) {
1025: Object[] array1 = (Object[]) obj1;
1026: Object[] array2 = (Object[]) obj2;
1027: if (array1.length != array2.length)
1028: return false;
1029: for (int i = 0; i < array1.length; i++)
1030: if (!equals(array1[i], array2[i]))
1031: return false;
1032: return true;
1033: }
1034:
1035: if (Integer.TYPE.equals(cType)) {
1036: int[] array1 = (int[]) obj1;
1037: int[] array2 = (int[]) obj2;
1038: if (array1.length != array2.length)
1039: return false;
1040: for (int i = 0; i < array1.length; i++)
1041: if (array1[i] != array2[i])
1042: return false;
1043: return true;
1044: }
1045:
1046: if (Boolean.TYPE.equals(cType)) {
1047: boolean[] array1 = (boolean[]) obj1;
1048: boolean[] array2 = (boolean[]) obj2;
1049: if (array1.length != array2.length)
1050: return false;
1051: for (int i = 0; i < array1.length; i++)
1052: if (array1[i] != array2[i])
1053: return false;
1054: return true;
1055: }
1056:
1057: if (Long.TYPE.equals(cType)) {
1058: long[] array1 = (long[]) obj1;
1059: long[] array2 = (long[]) obj2;
1060: if (array1.length != array2.length)
1061: return false;
1062: for (int i = 0; i < array1.length; i++)
1063: if (array1[i] != array2[i])
1064: return false;
1065: return true;
1066: }
1067:
1068: if (Double.TYPE.equals(cType)) {
1069: double[] array1 = (double[]) obj1;
1070: double[] array2 = (double[]) obj2;
1071: if (array1.length != array2.length)
1072: return false;
1073: for (int i = 0; i < array1.length; i++)
1074: if (array1[i] != array2[i])
1075: return false;
1076: return true;
1077: }
1078:
1079: if (Byte.TYPE.equals(cType)) {
1080: byte[] array1 = (byte[]) obj1;
1081: byte[] array2 = (byte[]) obj2;
1082: if (array1.length != array2.length)
1083: return false;
1084: for (int i = 0; i < array1.length; i++)
1085: if (array1[i] != array2[i])
1086: return false;
1087: return true;
1088: }
1089:
1090: if (Character.TYPE.equals(cType)) {
1091: char[] array1 = (char[]) obj1;
1092: char[] array2 = (char[]) obj2;
1093: if (array1.length != array2.length)
1094: return false;
1095: for (int i = 0; i < array1.length; i++)
1096: if (array1[i] != array2[i])
1097: return false;
1098: return true;
1099: }
1100:
1101: if (Float.TYPE.equals(cType)) {
1102: float[] array1 = (float[]) obj1;
1103: float[] array2 = (float[]) obj2;
1104: if (array1.length != array2.length)
1105: return false;
1106: for (int i = 0; i < array1.length; i++)
1107: if (array1[i] != array2[i])
1108: return false;
1109: return true;
1110: }
1111:
1112: if (Short.TYPE.equals(cType)) {
1113: short[] array1 = (short[]) obj1;
1114: short[] array2 = (short[]) obj2;
1115: if (array1.length != array2.length)
1116: return false;
1117: for (int i = 0; i < array1.length; i++)
1118: if (array1[i] != array2[i])
1119: return false;
1120: return true;
1121: }
1122:
1123: return false;
1124: }
1125:
1126: /* private void settleDesignValueListener(Object oldVal, Object newVal) {
1127: if (oldVal == newVal) return;
1128:
1129: if (oldVal instanceof FormDesignValue.Listener && designValueListener != null)
1130: ((FormDesignValue.Listener)oldVal).removeChangeListener(designValueListener);
1131:
1132: if (newVal instanceof FormDesignValue.Listener) {
1133: if (designValueListener == null)
1134: designValueListener = new DesignValueListener();
1135: ((FormDesignValue.Listener)newVal).addChangeListener(designValueListener);
1136: }
1137: }
1138:
1139: class DesignValueListener implements javax.swing.event.ChangeListener {
1140: public void stateChanged(javax.swing.event.ChangeEvent e) {
1141: if (valueSet && propertyValue == e.getSource())
1142: try {
1143: setValue(propertyValue);
1144: }
1145: catch (Exception ex) { // can't do nothing here
1146: }
1147: }
1148: } */
1149:
1150: // -----
1151: /**
1152: * Convertor can be registered on a property and change value comming to
1153: * setValue method to something else. Used for automatic i18n.
1154: */
1155: public interface ValueConvertor {
1156: public Object convert(Object value, FormProperty property);
1157: }
1158:
1159: // ------------
1160:
1161: public static final class ValueWithEditor {
1162: private Object value;
1163: private PropertyEditor propertyEditor;
1164: private int propertyEditorIndex;
1165:
1166: public ValueWithEditor(Object value,
1167: PropertyEditor propertyEditor) {
1168: this .value = value;
1169: this .propertyEditor = propertyEditor;
1170: }
1171:
1172: ValueWithEditor(Object value, int propertyEditorIndex) {
1173: this .value = value;
1174: this .propertyEditorIndex = propertyEditorIndex;
1175: }
1176:
1177: public Object getValue() {
1178: return value;
1179: }
1180:
1181: public PropertyEditor getPropertyEditor() {
1182: return propertyEditor;
1183: }
1184:
1185: PropertyEditor getPropertyEditor(FormProperty property) {
1186: if (propertyEditor != null)
1187: return propertyEditor;
1188: if (propertyEditorIndex < 0)
1189: return null;
1190:
1191: PropertyEditor pe = property.getPropertyEditor();
1192: if (pe instanceof FormPropertyEditor) {
1193: FormPropertyEditor fpe = (FormPropertyEditor) pe;
1194: PropertyEditor[] allEds = fpe.getAllEditors();
1195: if (propertyEditorIndex < allEds.length)
1196: return allEds[propertyEditorIndex];
1197: }
1198:
1199: return null;
1200: }
1201: }
1202:
1203: public static Object getEnclosedValue(Object value) {
1204: return value instanceof ValueWithEditor ? ((ValueWithEditor) value)
1205: .getValue()
1206: : value;
1207: }
1208:
1209: // ------------
1210:
1211: public static interface Filter {
1212: public boolean accept(FormProperty property);
1213: }
1214:
1215: public static final Filter CHANGED_PROPERTY_FILTER = new Filter() {
1216: public boolean accept(FormProperty property) {
1217: return property.isChanged();
1218: }
1219: };
1220: }
|