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.lang.reflect.Field;
045:
046: import javax.faces.context.FacesContext;
047: import javax.faces.el.ValueBinding;
048:
049: import org.openide.util.NbBundle;
050: import org.netbeans.modules.visualweb.extension.openide.util.Trace;
051:
052: import com.sun.rave.designtime.DesignBean;
053: import com.sun.rave.designtime.faces.FacesDesignContext;
054: import org.netbeans.modules.visualweb.insync.UndoEvent;
055: import org.netbeans.modules.visualweb.insync.beans.Bean;
056: import org.netbeans.modules.visualweb.insync.beans.Property;
057: import org.netbeans.modules.visualweb.insync.models.FacesModelSet;
058: import org.netbeans.modules.web.jsf.api.facesmodel.ManagedBean.Scope;
059:
060: /**
061: * DesignProperty implementation based on delegation to beans.Property and subclasses, using Java
062: * and/or markup source.
063: *
064: * @author Carl Quinn
065: */
066: public class BeansDesignProperty extends SourceDesignProperty {
067:
068: public static final Object FROMSOURCE_UNKNOWNVALUE = new Object();
069:
070: Property property; // the persisted property state if set, i.e. not default, null if not set
071:
072: //--------------------------------------------------------------------------------- Construction
073:
074: /**
075: * @param descriptor
076: * @param lbean
077: */
078: BeansDesignProperty(PropertyDescriptor descriptor,
079: BeansDesignBean lbean) {
080: super (descriptor, lbean);
081: property = lbean.bean.getProperty(descriptor.getName());
082: }
083:
084: /**
085: *
086: */
087: protected void initLive() {
088: ClassLoader oldContextClassLoader = Thread.currentThread()
089: .getContextClassLoader();
090: try {
091: Thread.currentThread().setContextClassLoader(
092: ((LiveUnit) getDesignBean().getDesignContext())
093: .getBeansUnit().getClassLoader());
094: if (property != null) {
095: // try to get value object from down below. Will return primitives or bean
096: Object value = property.getValue(descriptor
097: .getPropertyType());
098:
099: // if not known by Property, then use live knowledge to attempt conversion
100: if (value == null) {
101: String valueSource = property.getValueSource();
102: assert Trace.trace("insync.live", "JLP.restoring "
103: + descriptor.getName() + " from \""
104: + valueSource + "\"");
105: value = fromSource(valueSource);
106: }
107: // if it's a bean, look up its live counterpart & use the instance
108: else if (value instanceof Bean) {
109: DesignBean lb = liveBean.unit
110: .getDesignBean((Bean) value);
111: value = lb.getInstance();
112: }
113: // else others are values ready to use
114:
115: assert Trace.trace("insync.live", "JLP.restoring "
116: + descriptor.getName()
117: + " as "
118: + value
119: + (value != null ? (" ("
120: + value.getClass().getName() + ")")
121: : ""));
122: invokeSetter(value);
123: }
124: } finally {
125: Thread.currentThread().setContextClassLoader(
126: oldContextClassLoader);
127: }
128: }
129:
130: /*
131: * @see org.netbeans.modules.visualweb.insync.live.SourceDesignProperty#getClipImage()
132: */
133: public ClipImage getClipImage() {
134: String name = descriptor.getName();
135: if (!isModified() || name.equals("id")) // don't bother saving id props
136: return null;
137: return new ClipImage(name, getValue());
138: }
139:
140: //----------------------------------------------------------------------------------- Conversion
141:
142: /**
143: * An extended Class.isAssignableFrom() that treats primitive lvalues as their object
144: * counterpart.
145: *
146: * @param to
147: * @param from
148: * @return
149: */
150: public static boolean isAssignableFrom(Class to, Class from) {
151: if (to.isPrimitive()) {
152: if (to == Boolean.TYPE)
153: to = Boolean.class;
154: else if (to == Character.TYPE)
155: to = Character.class;
156: else if (to == Byte.TYPE)
157: to = Byte.class;
158: else if (to == Short.TYPE)
159: to = Short.class;
160: else if (to == Integer.TYPE)
161: to = Integer.class;
162: else if (to == Long.TYPE)
163: to = Long.class;
164: else if (to == Float.TYPE)
165: to = Float.class;
166: else if (to == Double.TYPE)
167: to = Double.class;
168: }
169: return to.isAssignableFrom(from);
170: }
171:
172: /**
173: * Try to convert an object to source: markup or java.
174: *
175: * @param value
176: * @return
177: */
178: protected String toSource(Object value) {
179: boolean isMarkup = isMarkupSource();
180: //System.err.println("JLP.toSource: " + liveBean.getInstanceName() + "." + descriptor.getName() + " to:" + value);
181: //System.err.println("JLP.toSource: isMarkup:" + isMarkup);
182: if (value == null)
183: return isMarkup ? null : "null";
184:
185: if (isMarkup
186: || isAssignableFrom(descriptor.getPropertyType(), value
187: .getClass())) {
188: // Look locally first as an optimization
189: DesignBean lb = liveBean.unit.getBeanForInstance(value);
190:
191: //VB expressions cannot be used to refer the properties in the same bean
192: //when the initializers are added into the constructor
193: //This is required for migrated reef projects
194: if ((liveBean.unit.getBeansUnit().getPropertiesInitMethod()
195: .isConstructor())
196: && lb != null) {
197: return lb.getInstanceName();
198: }
199:
200: if (lb == null) {
201: // Now look through all contexts and see if we can find it
202: // !EAT TODONOW Optimize this, so that lookup for simple values like strings dont take too long ?
203: LiveUnit[] units = (LiveUnit[]) ((FacesModelSet) liveBean.unit
204: .getModel().getOwner())
205: .findDesignContexts(new String[] {
206: Scope.REQUEST.toString(),
207: Scope.SESSION.toString(),
208: Scope.APPLICATION.toString() });
209: for (int i = 0; i < units.length; i++) {
210: lb = units[i].getBeanForInstance(value);
211: if (lb != null)
212: break;
213: }
214: }
215:
216: if (lb != null) {
217: String binding = "#{"
218: + ((FacesDesignContext) lb.getDesignContext())
219: .getReferenceName() + "."
220: + lb.getInstanceName() + "}";
221: if (isMarkup)
222: return binding;
223: String typeName = getPropertyDescriptor()
224: .getPropertyType().getCanonicalName();
225: return "(" + typeName + ")getValue(\"" + binding
226: + "\")";
227: }
228: //System.err.println("JLP.toSource: valueSource:" + (isMarkup ? value.toString() : toJavaInitializationString(value)));
229: return isMarkup ? value.toString()
230: : toJavaInitializationString(value);
231: }
232: return "null/*!!BAD PROPERTY TYPE SET!!*/";
233: }
234:
235: /**
236: * Try to convert source, markup or java, to an object.
237: *
238: * @param sourceValue
239: * @return
240: */
241: protected Object fromSource(String sourceValue) {
242: Object result = fromSourceIncludeUnknown(sourceValue);
243: if (result == FROMSOURCE_UNKNOWNVALUE)
244: return null;
245: return result;
246: }
247:
248: /**
249: * Try to convert source, markup or java, to an object.
250: * If I am unable to compute the value, then return FROMSOUR_UNKNOWNVALUE.
251: * @param sourceValue
252: * @return
253: */
254: protected Object fromSourceIncludeUnknown(String sourceValue) {
255: boolean isMarkup = isMarkupSource();
256: if (isMarkup)
257: return sourceValue;
258:
259: Object value = FROMSOURCE_UNKNOWNVALUE;
260: try {
261: // Look for (java.sql.ResultSet)getValue("#{SessionBean1.personRowSet1}")
262: // !EAT TODO need to create a subclass of BeansDesignProperty that does the VB stuff, since this
263: // is Faces dependent
264: String lookFor = "getValue(\"#{";
265: int index = sourceValue.indexOf(lookFor);
266: if (index != -1) {
267: int lastIndex = sourceValue.indexOf("}", index);
268: if (lastIndex != -1) {
269: String vbString = sourceValue.substring(index
270: + lookFor.length() - 2, lastIndex + 1);
271: ValueBinding vb = FacesContext.getCurrentInstance()
272: .getApplication().createValueBinding(
273: vbString);
274: value = vb.getValue(FacesContext
275: .getCurrentInstance());
276: if (value == null) {
277: //Workaround for #120251
278: //It seems that the value will be null when the evaluation of an EL expression is
279: //trigerred by evaluation of another EL expression where-in the first part of the
280: //expression is same for both. It is strange that no exception is thrown in this
281: //scenario. Trying again work fine
282: value = vb.getValue(FacesContext
283: .getCurrentInstance());
284: }
285: return value;
286: }
287: }
288: // see if it is class.FIELD constant reference
289: int rpart = sourceValue.lastIndexOf('.');
290: if (rpart != -1) {
291: String cname = sourceValue.substring(0, rpart).trim();
292: String fname = sourceValue.substring(rpart + 1);
293: // Check to see if sourceValue is something like getSessionBean1().getGreeterClient1()
294: // We should try to handle this kind of case, but it will have to wait
295: // !EAT TODO XXX
296: if (cname.endsWith(")")) {
297: } else {
298: //System.err.println("JLP.fromSource cname:" + cname + " fname:" + fname);
299: Class c = liveBean.unit.sourceUnit
300: .getBeanClass(cname);
301: // Check for case where we have something like objectListDataProvider1.setObjectType(webapplication1.Name.class);
302: if (fname.equals("class")) // NOI18N
303: value = c;
304: else {
305: Field f = c.getField(fname);
306: value = f.get(null);
307: }
308: }
309: }
310: // see if it is a sibling bean reference
311: else {
312: DesignBean lb = liveBean.unit
313: .getBeanByName(sourceValue);
314: if (lb != null) {
315: value = lb.getInstance();
316: }
317: }
318: } catch (Exception e) {
319: System.err.println("Caught " + e + " in JLP.fromSource: "
320: + descriptor.getName());
321: e.printStackTrace();
322: }
323: //System.err.println("JLP.fromSource value:" + value);
324: return value;
325: }
326:
327: //-------------------------------------------------------------------------------------- Getters
328:
329: /*
330: * @see com.sun.rave.designtime.DesignProperty#getValueSource()
331: */
332: public String getValueSource() {
333: ClassLoader oldContextClassLoader = Thread.currentThread()
334: .getContextClassLoader();
335: try {
336: Thread.currentThread().setContextClassLoader(
337: ((LiveUnit) getDesignBean().getDesignContext())
338: .getBeansUnit().getClassLoader());
339: return property != null ? property.getValueSource() : null;
340: } finally {
341: Thread.currentThread().setContextClassLoader(
342: oldContextClassLoader);
343: }
344: }
345:
346: //-------------------------------------------------------------------------------------- Setters
347:
348: /**
349: * Pass a value (in object and/or source form) to our bean property, creating it as needed.
350: *
351: * @param value
352: * @param valueSource
353: */
354: protected void setBeanProperty(Object valueOrUnknown,
355: String valueSource) {
356:
357: Object value;
358: if (valueOrUnknown == FROMSOURCE_UNKNOWNVALUE)
359: value = null;
360: else
361: value = valueOrUnknown;
362: // intercept values that are the same as our initial value, and remove the source instead
363: if (valueOrUnknown != FROMSOURCE_UNKNOWNVALUE
364: && equalsInitial(value)) {
365: unsetBeanProperty();
366: return;
367: }
368: // could intercept unnecessary sets and just return
369: //if (objectsEqual(value, getValue())
370: // return;
371:
372: // real values need to be saved in source
373: UndoEvent event = null;
374: try {
375: String propname = getPropertyDescriptor().getName();
376: String description = NbBundle.getMessage(
377: BeansDesignProperty.class, "SetProperty", propname); // NOI18N
378: event = liveBean.unit.model.writeLock(description);
379:
380: // valueSource being null indicates that we should remove the property
381: if (valueSource == null) {
382: if (property == null) {
383: // just leave unset if already null
384: } else {
385: BeansDesignBean jlbean = (BeansDesignBean) liveBean;
386: jlbean.bean.unsetProperty(property);
387: property = null;
388: }
389: }
390: // valueSource being non-null indicates that we should add or update the property
391: else {
392: // replace live instances with their beans Bean to let it generate the identifier
393: if (value != null) {
394: BeansDesignBean vlb = (BeansDesignBean) liveBean.unit
395: .getBeanForInstance(value);
396: if (vlb != null)
397: value = vlb.bean;
398: }
399: if (property == null) {
400: BeansDesignBean jlbean = (BeansDesignBean) liveBean;
401: String name = getPropertyDescriptor().getName();
402: property = jlbean.bean.setProperty(name,
403: valueOrUnknown, valueSource);
404: } else {
405: property.setValue(valueOrUnknown, valueSource);
406: }
407: }
408: } finally {
409: liveBean.unit.model.writeUnlock(event);
410: }
411: }
412:
413: /**
414: * Unset our bean property, as needed.
415: */
416: protected void unsetBeanProperty() {
417: if (property != null) {
418: UndoEvent event = null;
419: try {
420: String propname = getPropertyDescriptor().getName();
421: String description = NbBundle.getMessage(
422: BeansDesignProperty.class, "UnsetProperty",
423: propname); // NOI18N
424: event = liveBean.unit.model.writeLock(description);
425:
426: BeansDesignBean jlbean = (BeansDesignBean) liveBean;
427: jlbean.bean.unsetProperty(property);
428: property = null;
429: } finally {
430: liveBean.unit.model.writeUnlock(event);
431: }
432: }
433: modified = false; // no longer modified, but no need to reacquire
434: }
435:
436: /*
437: * @see com.sun.rave.designtime.DesignProperty#setValue(java.lang.Object)
438: */
439: public boolean setValue(Object value) {
440: assert Trace.trace("insync.live", "JLP.setValue "
441: + descriptor.getName() + " from:" + getValue() + " to:"
442: + value);
443: Object oldValue = invokeGetter();
444: boolean result = invokeSetter(value);
445: if (result) {
446: setBeanProperty(value, toSource(value));
447: liveBean.fireDesignPropertyChanged(this , oldValue);
448: }
449: return result;
450: }
451:
452: // A fake object sent in place of old value in the property change event
453: // when the property state changes non-bound to bound or bound to non-bound
454: // to force the listeners to process the event fully
455: private static final Object NOT_EQUAL = new Object() {
456: public boolean equals(Object obj) {
457: return false;
458: };
459: };
460:
461: /*
462: * @see com.sun.rave.designtime.DesignProperty#setValueSource(java.lang.String)
463: */
464: public boolean setValueSource(String valueSource) {
465: boolean wasBound = isBindingValue(getValueSource());
466: Object oldValue = invokeGetter();
467: if (valueSource == null) {
468: return unset();
469: }
470: Object value = fromSourceIncludeUnknown(valueSource);
471: //System.err.println("JLP.setValueSource " + liveBean.getInstanceName() + "." +descriptor.getName() +
472: // " from:" + getValue() +
473: // " to src:\"" + valueSource + "\" val:" + value +
474: // (value != null ? (" class:" + value.getClass().getName()) : ""));
475: setBeanProperty(value, valueSource);
476: if (value == FROMSOURCE_UNKNOWNVALUE)
477: value = null;
478: boolean result = invokeSetter(value);
479: if (result) {
480: boolean isBound = isBindingValue(getValueSource());
481: liveBean.fireDesignPropertyChanged(this ,
482: ((isBound == wasBound) ? oldValue : NOT_EQUAL));
483: }
484: return result;
485: }
486:
487: //----------------------------------------------------------------------------------------- Misc
488:
489: /**
490: * @return true if this property is managed in markup, false if in java.
491: */
492: public boolean isMarkupSource() {
493: return property != null ? property.isMarkupProperty()
494: : ((BeansDesignBean) liveBean)
495: .isMarkupProperty(descriptor);
496: }
497:
498: /*
499: * @see com.sun.rave.designtime.DesignProperty#isModified()
500: */
501: public boolean isModified() {
502: boolean mod = super .isModified();
503: if (mod != (property != null)) {
504: assert Trace.trace("insync.live",
505: "JLP.isModified State out of sync! mod:" + mod
506: + " prop:" + property);
507: }
508: return mod; //!CQ XXX or just return property != null ???
509: }
510:
511: /*
512: * @see com.sun.rave.designtime.DesignProperty#unset()
513: */
514: public boolean unset() {
515: Object oldValue = invokeGetter();
516: boolean ok = super .unset();
517: if (ok) { //!CQ XXX maybe revert before/even if super fails?
518: unsetBeanProperty();
519: liveBean.fireDesignPropertyChanged(this , oldValue);
520: }
521: return ok;
522: }
523:
524: /**
525: * @return whether or not the value string is EL value or method binding expression
526: */
527: public static final boolean isBindingValue(String value) {
528: return value != null && value.startsWith("#{")
529: && value.endsWith("}");
530: }
531:
532: //--------------------------------------------------------------------------------------- Object
533:
534: /**
535: *
536: */
537: public String toString() {
538: return "[BLP name:" + descriptor.getName() + " value:"
539: + getValue() + " valueSource:" + getValueSource() + "]";
540: }
541: }
|