001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.form;
043:
044: import java.beans.*;
045: import java.lang.reflect.*;
046: import org.netbeans.modules.form.editors.AbstractFormatterFactoryEditor;
047: import org.openide.ErrorManager;
048:
049: import org.netbeans.modules.form.editors.*;
050: import org.netbeans.modules.form.editors2.JTableSelectionModelEditor;
051: import org.netbeans.modules.form.fakepeer.FakePeerSupport;
052:
053: /**
054: * Implementation of properties for (meta)components (class RADComponent).
055: * RADComponent is used to get the component instance and
056: * PropertyDescriptor provides read and write methods to get and set
057: * property values.
058: *
059: * @author Tomas Pavek
060: */
061: public class RADProperty extends FormProperty {
062:
063: public static final String SYNTH_PREFIX = "$$$_"; // NOI18N
064: public static final String SYNTH_PRE_CODE = SYNTH_PREFIX
065: + PROP_PRE_CODE + "_"; // NOI18N
066: public static final String SYNTH_POST_CODE = SYNTH_PREFIX
067: + PROP_POST_CODE + "_"; // NOI18N
068:
069: private RADComponent component;
070: private PropertyDescriptor desc;
071: private Object defaultValue;
072:
073: public RADProperty(RADComponent metacomp,
074: PropertyDescriptor propdesc) {
075: super (
076: new FormPropertyContext.Component(metacomp),//new RADPropertyContext(metacomp),
077: propdesc.getName(), propdesc.getPropertyType(),
078: propdesc.getDisplayName(), propdesc
079: .getShortDescription());
080:
081: component = metacomp;
082: desc = propdesc;
083:
084: if (desc.getWriteMethod() == null) {
085: setAccessType(NO_WRITE);
086: } else if (desc.getReadMethod() == null) {
087: setAccessType(DETACHED_READ);
088: } // assuming a bean property is at least readable or writeable
089:
090: defaultValue = BeanSupport.NO_VALUE;
091: if (canReadFromTarget()) {
092: try {
093: defaultValue = getTargetValue();
094: } catch (Exception ex) {
095: }
096: }
097: }
098:
099: public RADComponent getRADComponent() {
100: return component;
101: }
102:
103: public PropertyDescriptor getPropertyDescriptor() {
104: return desc;
105: }
106:
107: // -------------------------------
108:
109: public Object getTargetValue() throws IllegalAccessException,
110: InvocationTargetException {
111: Method readMethod = desc.getReadMethod();
112: if (readMethod == null) {
113: throw new IllegalAccessException(
114: "Not a readable property: " + desc.getName()); // NOI18N
115: }
116: return readMethod.invoke(component.getBeanInstance(),
117: new Object[0]);
118: }
119:
120: public void setTargetValue(Object value)
121: throws IllegalAccessException, IllegalArgumentException,
122: InvocationTargetException {
123: Method writeMethod = desc.getWriteMethod();
124: if (writeMethod == null) {
125: throw new IllegalAccessException(
126: "Not a writeable property: " + desc.getName()); // NOI18N
127: }
128:
129: Object beanInstance = component.getBeanInstance();
130:
131: // Ugly hack for Scrollbar - Scrollbar.setOrientation(...) method tries
132: // to re-create the (native) peer, which we cannot allow. So we detach
133: // the peer first before calling the method. This is the only place
134: // where we can do it. It could be probably done for all AWT
135: // components, but I don't know about any other which would need it.
136: java.awt.peer.ComponentPeer scrollbarPeerHack = "setOrientation"
137: .equals(writeMethod.getName()) // NOI18N
138: && beanInstance instanceof java.awt.Scrollbar ? FakePeerSupport
139: .detachFakePeer((java.awt.Component) beanInstance)
140: : null;
141:
142: try {
143: // invoke the setter method
144: writeMethod.invoke(component.getBeanInstance(),
145: new Object[] { value });
146: } catch (InvocationTargetException ex) {
147: // annotate exception
148: String message = FormUtils.getFormattedBundleString(
149: "MSG_ERR_WRITING_TO_PROPERTY", // NOI18N
150: new Object[] { getDisplayName() });
151:
152: Throwable tex = ex.getTargetException();
153: if (tex instanceof IllegalArgumentException) {
154: ErrorManager.getDefault()
155: .annotate(tex, ErrorManager.WARNING, null,
156: message, null, null);
157: // Issue 73627
158: if ("contentType".equals(getName())
159: && (beanInstance instanceof javax.swing.JTextPane)) { // NOI18N
160: return;
161: }
162: throw (IllegalArgumentException) tex;
163: } else if (tex instanceof IllegalAccessException) {
164: ErrorManager.getDefault()
165: .annotate(tex, ErrorManager.WARNING, null,
166: message, null, null);
167: throw (IllegalAccessException) tex;
168: } else if (value == null
169: && tex instanceof NullPointerException) {
170: IllegalArgumentException iae = new IllegalArgumentException();
171: ErrorManager.getDefault()
172: .annotate(iae, ErrorManager.WARNING, null,
173: message, null, null);
174: throw iae;
175: }
176:
177: ErrorManager.getDefault().annotate(ex,
178: ErrorManager.WARNING, null, message, null, null);
179:
180: throw ex;
181: }
182:
183: if (scrollbarPeerHack != null) // restore the Scrollbar's fake peer
184: FakePeerSupport.attachFakePeer(
185: (java.awt.Component) beanInstance,
186: scrollbarPeerHack);
187: }
188:
189: @Override
190: protected Object getRealValue(Object value) {
191: Object realValue = super .getRealValue(value);
192:
193: if (realValue == FormDesignValue.IGNORED_VALUE) {
194: Object instance = component.getBeanInstance();
195: String propName = desc.getName();
196: if (instance instanceof java.awt.Component
197: && "text".equals(propName)) { // NOI18N
198: realValue = ((FormDesignValue) value).getDescription();
199: } else if (supportsDefaultValue()
200: && !((instance instanceof javax.swing.JEditorPane) && "page"
201: .equals(propName))) { // Issue 123303 // NOI18N
202: // Issue 87647
203: return getDefaultValue();
204: }
205: }
206:
207: return realValue;
208: }
209:
210: @Override
211: public boolean supportsDefaultValue() {
212: return defaultValue != BeanSupport.NO_VALUE;
213: }
214:
215: @Override
216: public Object getDefaultValue() {
217: Object specialDefaultValue = FormUtils
218: .getSpecialDefaultPropertyValue(component
219: .getBeanInstance(), getName());
220: return specialDefaultValue != BeanSupport.NO_VALUE ? specialDefaultValue
221: : defaultValue;
222: }
223:
224: // ----------
225:
226: @Override
227: public boolean canWrite() {
228: return component.isReadOnly() ? false : super .canWrite();
229: }
230:
231: // ----------
232:
233: @Override
234: public PropertyEditor getExpliciteEditor() {
235: PropertyEditor prEd = null;
236:
237: PropertyDescriptor descriptor = getPropertyDescriptor();
238: if (descriptor.getPropertyType() == Integer.TYPE
239: && ("mnemonic".equals(descriptor.getName()) // NOI18N
240: || "displayedMnemonic".equals(descriptor.getName()))) { // NOI18N
241: prEd = new MnemonicEditor();
242: } else {
243: if ("editor".equals(descriptor.getName())
244: && (javax.swing.JSpinner.class
245: .isAssignableFrom(component.getBeanClass()))) { // NOI18N
246: prEd = new SpinnerEditorEditor();
247: } else if ("formatterFactory".equals(descriptor.getName())
248: && (javax.swing.JFormattedTextField.class
249: .isAssignableFrom(component.getBeanClass()))) { // NOI18N
250: prEd = new AbstractFormatterFactoryEditor();
251: } else if ("selectionModel".equals(descriptor.getName())
252: && (javax.swing.JTable.class.equals(component
253: .getBeanClass()))) { // NOI18N
254: prEd = new JTableSelectionModelEditor();
255: } else {
256: prEd = createEnumEditor(descriptor);
257: }
258: }
259:
260: if (prEd == null) {
261: try {
262: prEd = desc.createPropertyEditor(component
263: .getBeanInstance());
264: } catch (Exception ex) {
265: org.openide.ErrorManager.getDefault().notify(
266: org.openide.ErrorManager.INFORMATIONAL, ex);
267: }
268: }
269:
270: return prEd;
271: }
272:
273: protected PropertyEditor createEnumEditor(
274: PropertyDescriptor descriptor) {
275: Object[] enumerationValues;
276:
277: if (!"debugGraphicsOptions".equals(descriptor.getName()) // NOI18N
278: || !javax.swing.JComponent.class
279: .isAssignableFrom(component.getBeanClass())) { // get the enumeration values by standard means
280: enumerationValues = (Object[]) descriptor
281: .getValue("enumerationValues"); // NOI18N
282: } else { // hack: debugGraphicsOptions is problematic because its
283: // default value (0) does not correspond to any of the
284: // enumerated constants (NONE_OPTION is -1)
285: enumerationValues = new Object[] {
286: "NONE_OPTION",
287: new Integer(-1),
288: "DebugGraphics.NONE_OPTION", // NOI18N
289: "NO_CHANGES",
290: new Integer(0),
291: "0", // NOI18N
292: "LOG_OPTION",
293: new Integer(1),
294: "DebugGraphics.LOG_OPTION", // NOI18N
295: "FLASH_OPTION",
296: new Integer(2),
297: "DebugGraphics.FLASH_OPTION", // NOI18N
298: "BUFFERED_OPTION", new Integer(4),
299: "DebugGraphics.BUFFERED_OPTION" }; // NOI18N
300: }
301:
302: if (enumerationValues == null
303: && "defaultCloseOperation".equals(descriptor.getName()) // NOI18N
304: && (javax.swing.JDialog.class
305: .isAssignableFrom(component.getBeanClass()) || javax.swing.JInternalFrame.class
306: .isAssignableFrom(component.getBeanClass()))) { // hack: enumeration definition is missing in standard Swing
307: // for JDialog and JInternalFrame defaultCloseOperation property
308: enumerationValues = new Object[] { "DISPOSE_ON_CLOSE",
309: new Integer(2), // NOI18N
310: "WindowConstants.DISPOSE_ON_CLOSE", // NOI18N
311: "DO_NOTHING_ON_CLOSE", new Integer(0), // NOI18N
312: "WindowConstants.DO_NOTHING_ON_CLOSE", // NOI18N
313: "HIDE_ON_CLOSE", new Integer(1), // NOI18N
314: "WindowConstants.HIDE_ON_CLOSE" }; // NOI18N
315: }
316:
317: return enumerationValues != null ? new EnumEditor(
318: enumerationValues) : null;
319: }
320:
321: @Override
322: protected Method getWriteMethod() {
323: return desc.getWriteMethod();
324: }
325:
326: @Override
327: public void setPreCode(String value) {
328: if ((preCode == null && value != null)
329: || (preCode != null && !preCode.equals(value))) {
330: Object old = preCode;
331: preCode = value;
332: if (isChangeFiring() && component.getFormModel() != null)
333: component.getFormModel().fireSyntheticPropertyChanged(
334: component, SYNTH_PRE_CODE + getName(), old,
335: value);
336: }
337: }
338:
339: @Override
340: public void setPostCode(String value) {
341: if ((postCode == null && value != null)
342: || (postCode != null && !postCode.equals(value))) {
343: Object old = postCode;
344: postCode = value;
345: if (isChangeFiring() && component.getFormModel() != null)
346: component.getFormModel().fireSyntheticPropertyChanged(
347: component, SYNTH_POST_CODE + getName(), old,
348: value);
349: }
350: }
351:
352: // ----------------------------------
353:
354: /* protected void firePropertyValueChange(Object old, Object current) {
355: super.firePropertyValueChange(old, current);
356:
357: if (isChangeFiring() && component.getFormModel() != null)
358: component.getFormModel().fireComponentPropertyChanged(component,
359: desc.getName(), old, current);
360: }
361:
362: protected void fireCurrentEditorChange(PropertyEditor old, PropertyEditor current) {
363: super.fireCurrentEditorChange(old, current);
364:
365: if (isChangeFiring() && component.getFormModel() != null)
366: component.getFormModel().fireComponentPropertyChanged(component,
367: desc.getName(), null, null);
368: } */
369:
370: // -------------------
371: // innerclasses
372: // Descriptor for fake-properties (not real, design-time only) that
373: // need to pretend they are of certain type although without both
374: // getter and setter. Used e.g. by ButtonGroupProperty.
375: static class FakePropertyDescriptor extends PropertyDescriptor {
376: Class propType;
377:
378: FakePropertyDescriptor(String name, Class type)
379: throws IntrospectionException {
380: super (name, null, null);
381: propType = type;
382: }
383:
384: @Override
385: public Class getPropertyType() {
386: return propType;
387: }
388: }
389: }
|