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-2007 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: package org.netbeans.modules.visualweb.insync.live;
042:
043: import java.beans.PropertyDescriptor;
044: import java.beans.PropertyEditor;
045: import java.beans.PropertyEditorManager;
046: import java.lang.reflect.Method;
047: import org.netbeans.modules.visualweb.extension.openide.util.Trace;
048: import com.sun.rave.designtime.DesignBean;
049: import com.sun.rave.designtime.DesignProperty;
050: import com.sun.rave.designtime.PropertyEditor2;
051: import com.sun.rave.propertyeditors.resolver.PropertyEditorResolver;
052: import java.util.Collection;
053: import org.openide.util.Lookup;
054:
055: /**
056: * Abstract base partial DesignProperty implementation which manages a PropertyDescriptor and other
057: * basic beans and designtime stuff and ties into the rest of the SourceLive* classes.
058: *
059: * @author Joe Nuxoll
060: * @author Carl Quinn
061: */
062: public abstract class SourceDesignProperty implements DesignProperty {
063:
064: public static final SourceDesignProperty[] EMPTY_ARRAY = {};
065: public static final Object[] EMPTY_OBJECT_ARRAY = {};
066:
067: protected final PropertyDescriptor descriptor;
068: protected final SourceDesignBean liveBean;
069:
070: protected PropertyEditor editor;
071: protected boolean modified = false;
072: protected Object initialValue;
073: protected String category;
074:
075: //--------------------------------------------------------------------------------- Construction
076:
077: /**
078: * @param descriptor
079: * @param liveBean
080: */
081: protected SourceDesignProperty(PropertyDescriptor descriptor,
082: SourceDesignBean liveBean) {
083: this .descriptor = descriptor;
084: this .liveBean = liveBean;
085: if (descriptor.getWriteMethod() != null)
086: this .initialValue = getValue(); // don't bother getting initial value for read-only props
087: }
088:
089: /**
090: * Called after construction to initialize the live state from the source
091: */
092: protected abstract void initLive();
093:
094: /**
095: *
096: */
097: public static class ClipImage {
098: String name;
099: Object value;
100:
101: ClipImage(String name, Object value) {
102: this .name = name;
103: this .value = value;
104: }
105:
106: public String toString() {
107: StringBuffer sb = new StringBuffer();
108: toString(sb);
109: return sb.toString();
110: }
111:
112: public void toString(StringBuffer sb) {
113: sb.append("[DesignProperty.ClipImage");
114: sb.append(" name=" + name);
115: sb.append(" value=" + value);
116: sb.append("]");
117: }
118: }
119:
120: /**
121: * @return
122: */
123: public ClipImage getClipImage() {
124: if (!isModified())
125: return null;
126: return new ClipImage(descriptor.getName(), getValue());
127: }
128:
129: //------------------------------------------------------------------------------------ Accessors
130:
131: /**
132: * @param category
133: */
134: public void setPropertyCategory(String category) {
135: this .category = category;
136: }
137:
138: /**
139: * @return
140: */
141: public String getPropertyCategory() {
142: return category;
143: }
144:
145: /*
146: * @see com.sun.rave.designtime.DesignProperty#getPropertyDescriptor()
147: */
148: public PropertyDescriptor getPropertyDescriptor() {
149: return descriptor;
150: }
151:
152: /*
153: * @see com.sun.rave.designtime.DesignProperty#getDesignBean()
154: */
155: public DesignBean getDesignBean() {
156: return liveBean;
157: }
158:
159: private static Lookup.Result propertyEditorResolverLookupResult;
160:
161: /**
162: * Look up all property editor resolvers registered with the current IDE session.
163: */
164: private static PropertyEditorResolver[] getPropertyEditorResolvers() {
165: if (propertyEditorResolverLookupResult == null) {
166: Lookup.Template template = new Lookup.Template(
167: PropertyEditorResolver.class);
168: Lookup lookup = Lookup.getDefault();
169: propertyEditorResolverLookupResult = lookup
170: .lookup(template);
171: }
172: Collection instances = propertyEditorResolverLookupResult
173: .allInstances();
174: return (PropertyEditorResolver[]) instances
175: .toArray(new PropertyEditorResolver[instances.size()]);
176: }
177:
178: /**
179: * Load the property editor for use with this design property, and cache it.
180: */
181: protected void loadEditor() {
182: // Ask each property editor resolver service that was registered with the
183: // IDE for an editor appropriate for the property descriptor
184: if (editor == null) {
185: for (PropertyEditorResolver resolver : getPropertyEditorResolvers()) {
186: editor = resolver.getEditor(this .descriptor);
187: if (editor != null)
188: break;
189: }
190: }
191: // If no editor returned by a resolver, and the property descriptor has an
192: // editor class property, attempt to instantiate it
193: if (editor == null
194: && this .descriptor.getPropertyEditorClass() != null) {
195: try {
196: editor = (PropertyEditor) this .descriptor
197: .getPropertyEditorClass().newInstance();
198: } catch (Exception e) {
199: e.printStackTrace();
200: }
201: }
202: // If no editor found yet, ask the static Java Beans editor manager for an
203: // editor appropriate for the property descriptor
204: if (editor == null) {
205: editor = PropertyEditorManager.findEditor(descriptor
206: .getPropertyType());
207: }
208: // Finally, if no editor registered with the Java Beans editor manager, use
209: // an editor that allows sibling bean selection
210: if (editor == null) {
211: editor = new BeanSelectionEditor(this );
212: }
213: if (editor instanceof PropertyEditor2) {
214: try {
215: ((PropertyEditor2) editor).setDesignProperty(this );
216: } catch (Exception e) {
217: e.printStackTrace();
218: }
219: }
220: }
221:
222: /**
223: * Remove any property editor that may have been loaded prior.
224: */
225: protected void unloadPropertyEditor() {
226: editor = null;
227: }
228:
229: /**
230: * @return The property editor for this property, for internal use.
231: */
232: public PropertyEditor getPropertyEditor() {
233: if (editor == null)
234: loadEditor();
235: return editor;
236: }
237:
238: //-------------------------------------------------------------------------------------- Getters
239:
240: /**
241: * @return
242: */
243: protected Object invokeGetter() {
244: Method getter = descriptor.getReadMethod();
245: if (getter == null)
246: return null;
247:
248: try {
249: Object instance = liveBean.getInstance();
250: if (instance != null) {
251: Object value = getter.invoke(instance,
252: EMPTY_OBJECT_ARRAY);
253: assert Trace.trace("insync.live", "SLP.getValue "
254: + descriptor.getName() + " is:" + value);
255: return value;
256: }
257: } catch (Exception e) {
258: System.err.println("Caught "
259: + e
260: + " in SLP.invokeGetter "
261: + liveBean.beanInfo.getBeanDescriptor()
262: .getBeanClass().getName() + "."
263: + descriptor.getName());
264: //e.printStackTrace();
265: }
266: return null;
267: }
268:
269: /*
270: * @see com.sun.rave.designtime.DesignProperty#getValue()
271: */
272: public Object getValue() {
273: ClassLoader oldContextClassLoader = Thread.currentThread()
274: .getContextClassLoader();
275: try {
276: Thread.currentThread().setContextClassLoader(
277: ((LiveUnit) getDesignBean().getDesignContext())
278: .getBeansUnit().getClassLoader());
279: return invokeGetter();
280: } finally {
281: Thread.currentThread().setContextClassLoader(
282: oldContextClassLoader);
283: }
284: }
285:
286: /**
287: * Expected to be overriden by subclasses
288: */
289: public abstract String getValueSource();
290:
291: /**
292: * Calculate the Java source representation for a given object value using the property editor.
293: *
294: * @param value The value object.
295: * @return the Java initialization string for the value.
296: */
297: protected final String toJavaInitializationString(Object value) {
298: if (editor == null)
299: loadEditor();
300: if (editor != null) {
301: try {
302: editor.setValue(value);
303: String text = editor.getJavaInitializationString();
304: return text != null ? text : "null";
305: } catch (Exception e) {
306: System.err.println("Caught " + e
307: + " in SLP.toJavaInitializationString editor: "
308: + editor);
309: //e.printStackTrace();
310: }
311: }
312: return value != null ? String.valueOf(value) : "null";
313: }
314:
315: //-------------------------------------------------------------------------------------- Setters
316:
317: /**
318: * @param value
319: * @return
320: */
321: protected boolean invokeSetter(Object value) {
322: Method setter = descriptor.getWriteMethod();
323: if (setter == null)
324: return false;
325:
326: try {
327: setter.invoke(liveBean.getInstance(),
328: new Object[] { value });
329: setModified(true);
330: return true;
331: } catch (Exception e) {
332: System.err.print("Caught "
333: + e
334: + " in SLP.invokeSetter "
335: + liveBean.beanInfo.getBeanDescriptor()
336: .getBeanClass().getName() + "."
337: + descriptor.getName() + " to:" + value);
338: if (value != null)
339: System.err.println(" [" + value.getClass().getName()
340: + "]");
341: else
342: System.err.println();
343: //e.printStackTrace();
344: return false;
345: }
346: }
347:
348: /*
349: * @see com.sun.rave.designtime.DesignProperty#setValue(java.lang.Object)
350: */
351: public boolean setValue(Object value) {
352: ClassLoader oldContextClassLoader = Thread.currentThread()
353: .getContextClassLoader();
354: try {
355: Thread.currentThread().setContextClassLoader(
356: ((LiveUnit) getDesignBean().getDesignContext())
357: .getBeansUnit().getClassLoader());
358: Object oldValue = invokeGetter();
359: boolean ok = invokeSetter(value);
360: if (ok)
361: liveBean.fireDesignPropertyChanged(this , oldValue);
362: return ok;
363: } finally {
364: Thread.currentThread().setContextClassLoader(
365: oldContextClassLoader);
366: }
367: }
368:
369: /*
370: * @see com.sun.rave.designtime.DesignProperty#setValueSource(java.lang.String)
371: */
372: public abstract boolean setValueSource(String value);
373:
374: //--------------------------------------------------------------------------------- DesignProperty
375:
376: /**
377: * Set this property as modified or unmodified. If the latter, then also regrab the initial
378: * value.
379: *
380: * @param modified The modified state that this property should be set to.
381: */
382: public void setModified(boolean modified) {
383: this .modified = modified;
384: if (!modified)
385: initialValue = getValue();
386: }
387:
388: /**
389: * Returns whether or not two given values are equal, including null-ness
390: *
391: * @param v1 The first value.
392: * @param v2 The second value.
393: * @return True iff the two values are equal.
394: */
395: public static boolean objectsEqual(Object v1, Object v2) {
396: return (v1 == null) == (v2 == null)
397: && (v1 == null || v1.equals(v2));
398: }
399:
400: /**
401: * Returns whether or not a given value is differnt than the initial value
402: *
403: * @param value The value to compare against this properties initial value.
404: * @return True iff the value is equal to this properties initial value.
405: */
406: public boolean equalsInitial(Object value) {
407: return objectsEqual(initialValue, value);
408: }
409:
410: /*
411: * @see com.sun.rave.designtime.DesignProperty#isModified()
412: */
413: public boolean isModified() {
414: return modified;
415: }
416:
417: /*
418: * @see com.sun.rave.designtime.DesignProperty#getUnsetValue()
419: */
420: public Object getUnsetValue() {
421: return initialValue;
422: }
423:
424: /*
425: * @see com.sun.rave.designtime.DesignProperty#unset()
426: */
427: public boolean unset() {
428: boolean ok = setValue(initialValue);
429: if (ok)
430: modified = false; // no longer modified, but no need to reacquire
431: return ok;
432: }
433:
434: //--------------------------------------------------------------------------------------- Object
435:
436: /**
437: *
438: */
439: public String toString() {
440: return "[SLP name:" + descriptor.getName() + " value:"
441: + getValue() + "]";
442: }
443: }
|