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.faces;
042:
043: import java.beans.BeanInfo;
044: import java.beans.EventSetDescriptor;
045: import java.beans.PropertyDescriptor;
046: import java.util.Iterator;
047: import java.util.List;
048:
049: import javax.faces.component.UIComponent;
050: import javax.faces.el.ValueBinding;
051:
052: import org.w3c.dom.Element;
053: import org.w3c.dom.Node;
054:
055: import com.sun.rave.designtime.markup.AttributeDescriptor;
056: import com.sun.rave.designtime.Constants;
057: import com.sun.rave.designtime.DesignBean;
058: import com.sun.rave.designtime.Position;
059: import java.util.ArrayList;
060: import java.util.Collections;
061: import java.util.HashSet;
062: import java.util.Set;
063: import org.netbeans.modules.visualweb.insync.beans.Bean;
064: import org.netbeans.modules.visualweb.insync.beans.EventSet;
065: import org.netbeans.modules.visualweb.insync.beans.Property;
066: import org.openide.filesystems.FileObject;
067: import org.netbeans.modules.visualweb.insync.java.JavaClass.UsageStatus;
068:
069: /**
070: * A MarkupBean for a JSF component that lives in a FacesPageUnit.
071: */
072: public class FacesBean extends MarkupBean {
073:
074: public static final String BINDING_ATTR = "binding"; // NOI18N
075: public static final String ID_ATTR = "id"; // NOI18N
076:
077: Element facetElement; // if we are a facet, this is our wrapping facet element
078:
079: //--------------------------------------------------------------------------------- Construction
080:
081: /**
082: * Construct a bean bound to existing field & accessor methods, and page element.
083: *
084: * @param unit
085: * @param beanInfo
086: * @param name
087: * @param field
088: * @param getter
089: * @param setter
090: * @param element
091: */
092: FacesBean(FacesPageUnit unit, BeanInfo beanInfo, String name,
093: Element element) {
094: super (unit, beanInfo, name, element);
095: }
096:
097: /**
098: * Construct a new bean, creating the underlying field and accessor methods and using given page
099: * element
100: *
101: * @param unit
102: * @param beanInfo
103: * @param name
104: * @param parent
105: * @param element
106: */
107: FacesBean(FacesPageUnit unit, BeanInfo beanInfo, String name,
108: MarkupBean parent, Element element) {
109: super (unit, beanInfo, name, parent, element);
110: setProperty(ID_ATTR, null, getName());
111: // our DOM parent must be a facet tag if it is not our faces parent
112: if (parent != null && parent.element != element.getParentNode()
113: && parent.element.getNodeType() == Node.ELEMENT_NODE) {
114: // Parent might be a facet tag
115: Element parentElement = (Element) element.getParentNode();
116: if (FacesPageUnit.URI_JSF_CORE.equals(parentElement
117: .getNamespaceURI()))
118: this .facetElement = parentElement;
119: }
120: }
121:
122: //------------------------------------------------------------------------------------ Parenting
123:
124: /**
125: * Take the opportinuty to scan for and bind to this bean's parent.
126: * !CQ TODO: maybe share code with superclass...
127: *
128: * @return the parent of this bean iff not previously bound
129: */
130: public Bean bindParent() {
131: if (parent == null) {
132: //!CQ walk up element tree to find our parent
133: for (Node e = element.getParentNode(); e instanceof Element; e = e
134: .getParentNode()) {
135: parent = ((FacesPageUnit) unit)
136: .getMarkupBean((Element) e);
137: if (parent != null)
138: return parent;
139: if (e.getLocalName().equals("facet")
140: && e.getNamespaceURI().equals(
141: FacesPageUnit.URI_JSF_CORE))
142: facetElement = (Element) e;
143: }
144: }
145: return null;
146: }
147:
148: /*
149: * @see org.netbeans.modules.visualweb.insync.beans.Bean#performInstanceParenting(java.lang.Object, java.lang.Object, com.sun.rave.designtime.Position)
150: */
151: public boolean performInstanceParenting(Object instance,
152: Object parent, Position pos) {
153: if (instance instanceof UIComponent) {
154: if (parent instanceof UIComponent) {
155: String facetName = this .getFacetName();
156: if (facetName != null) {
157: ((UIComponent) parent).getFacets().put(facetName,
158: (UIComponent) instance);
159: } else {
160: int index = pos != null ? pos.getIndex() : -1;
161: List kids = ((UIComponent) parent).getChildren();
162: if (index >= 0 && index <= kids.size())
163: kids.add(index, instance);
164: else
165: kids.add(instance);
166: }
167: updateBindingLive(instance);
168: return true; // got a good parent
169: }
170: return false; // skip this one & try an ancestor
171: }
172: return true; // something is funky--just stop now
173: }
174:
175: /**
176: * Update the live faces value binding for this component
177: * @param instance
178: */
179: public void updateBindingLive(Object instance) {
180: if (instance instanceof UIComponent) {
181: UIComponent uic = (UIComponent) instance;
182: ValueBinding vb = uic.getValueBinding(BINDING_ATTR);
183: String binding = getCompBinding();
184: if (vb == null || !vb.getExpressionString().equals(binding)) {
185: vb = ((FacesPageUnit) unit).getFacesApplication()
186: .createValueBinding(binding);
187: uic.setValueBinding(BINDING_ATTR, vb);
188: }
189: }
190: }
191:
192: /*
193: * @see org.netbeans.modules.visualweb.insync.beans.Bean#performInstanceUnparenting(java.lang.Object, java.lang.Object)
194: */
195: public void performInstanceUnparenting(Object instance,
196: Object parent) {
197: if (parent instanceof UIComponent
198: && instance instanceof UIComponent)
199: ((UIComponent) parent).getChildren().remove(instance);
200: }
201:
202: //------------------------------------------------------------------------------------ Accessors
203:
204: /**
205: * Directly set the markup=>java bean binding attribute(s)
206: */
207: void setBindingProperty() {
208: String binding = getCompBinding();
209: setAttr(BINDING_ATTR, binding); // not a real property--just set the attr
210: }
211:
212: void clearBindingProperty() {
213: String binding = getCompBinding();
214: removeAttr(BINDING_ATTR); // not a real property--just set the attr
215: }
216:
217: /**
218: * Directly set the markup=>java bean binding attribute(s) and also the live instances
219: * @param liveBean
220: */
221: void setBindingPropertyLive(DesignBean liveBean) {
222: //Update the bindings only if the binding already exists for the bean
223: if (getAttr(BINDING_ATTR) != null) {
224: String binding = getCompBinding();
225: setAttr(BINDING_ATTR, binding); // not a real property--just set the attr
226: updateBindingLive(liveBean.getInstance());
227: }
228: }
229:
230: /**
231: * Set the name of this bean, making sure also to set the binding attr
232: * @see org.netbeans.modules.visualweb.insync.beans.Bean#setName(java.lang.String, boolean, com.sun.rave.designtime.DesignBean)
233: */
234: public String setName(String name, boolean autoNumber,
235: DesignBean liveBean) {
236: String oldname = getName();
237: String newname = super .setName(name, autoNumber, liveBean);
238: if (newname != null && !newname.equals(oldname)) {
239: setBindingPropertyLive(liveBean);
240: liveBean.getProperty(ID_ATTR).setValue(getName());
241: }
242: return newname;
243: }
244:
245: /**
246: * Get the binding expression string for this bean.
247: * @return the binding expression string for this bean.
248: */
249: public String getCompBinding() {
250: return ((FacesPageUnit) unit).getCompBinding(getName());
251: }
252:
253: /**
254: * Get the facet element if this bean is a facet.
255: * @return the facet element if this bean is a facet, or null if it is not.
256: */
257: public Element getFacetElement() {
258: return facetElement;
259: }
260:
261: /**
262: * Get the facet name if this bean is a facet.
263: * @return the facet name if this bean is a facet, or null if it is not.
264: */
265: public String getFacetName() {
266: return facetElement != null ? facetElement.getAttribute("name")
267: : null;
268: }
269:
270: //----------------------------------------------------------------------------------- Properties
271:
272: /**
273: * Determine if a given property should be managed in markup based on it having an attribute
274: * descriptor.
275: *
276: * @param pd The propertty's descriptor.
277: * @return The attribute descriptor if there is one, else null.
278: */
279: public static AttributeDescriptor getAttributeDescriptor(
280: PropertyDescriptor pd) {
281: Object ad = pd
282: .getValue(com.sun.rave.designtime.Constants.PropertyDescriptor.ATTRIBUTE_DESCRIPTOR);
283: if (ad instanceof AttributeDescriptor)
284: return (AttributeDescriptor) ad;
285: return null;
286: }
287:
288: /*
289: * @see org.netbeans.modules.visualweb.insync.beans.Bean#isMarkupProperty(java.beans.PropertyDescriptor)
290: */
291: public boolean isMarkupProperty(PropertyDescriptor pd) {
292: return getAttributeDescriptor(pd) != null;
293: }
294:
295: //------------------------------------------------------------------------------------ EventSets
296:
297: /**
298: * Perform markup-based event wiring here on a per-bean basis.
299: */
300: protected void bindEventSets() {
301: for (Iterator pi = properties.iterator(); pi.hasNext();) {
302: Property p = (Property) pi.next();
303: if (p instanceof MarkupProperty
304: && MethodBindEventSet.isMethodBindProperty(p)) {
305: MarkupProperty mp = (MarkupProperty) p;
306: PropertyDescriptor pd = p.getDescriptor();
307: EventSetDescriptor[] esds = beanInfo
308: .getEventSetDescriptors();
309: for (int i = 0; i < esds.length; i++) {
310: Object epdO = esds[i]
311: .getValue(Constants.EventSetDescriptor.BINDING_PROPERTY);
312: if (pd.equals(epdO))
313: eventSets.add(new MethodBindEventSet(this ,
314: esds[i], mp));
315: }
316: }
317: }
318: }
319:
320: /*
321: * @see org.netbeans.modules.visualweb.insync.beans.Bean#newCreatedEventSet(java.beans.EventSetDescriptor)
322: */
323: protected EventSet newCreatedEventSet(EventSetDescriptor esd) {
324: Object pdO = esd
325: .getValue(Constants.EventSetDescriptor.BINDING_PROPERTY);
326: if (pdO instanceof PropertyDescriptor) {
327: //!CQ check for attr descriptor too?
328: return new MethodBindEventSet(this , esd,
329: (PropertyDescriptor) pdO);
330: }
331: return super .newCreatedEventSet(esd);
332: }
333:
334: /*
335: * @see org.netbeans.modules.visualweb.insync.beans.Bean#newCreatedProperty(java.beans.PropertyDescriptor)
336: */
337: protected Property newCreatedProperty(PropertyDescriptor pd) {
338: if (!isMarkupProperty(pd)) {
339: addBinding();
340: }
341: return super .newCreatedProperty(pd);
342: }
343:
344: public void addBinding() {
345: if (!isInserted()) {
346: unit.addBindingBean(getName());
347: }
348: setBindingProperty();
349: }
350:
351: /*
352: * @return The usage info
353: */
354: public UsageInfo getUsageInfo() {
355: UsageStatus status = UsageStatus.NOT_USED;
356: if (isUsedInBindingExpression()) {
357: status = UsageStatus.USED;
358: }
359: if (status != UsageStatus.USED) {
360: List<FileObject> fObjs = new ArrayList<FileObject>();
361: fObjs.add(unit.getJavaUnit().getFileObject());
362: status = unit.getThisClass().isPropertyUsed(getName(),
363: fObjs);
364: if (status == UsageStatus.INIT_USE_ONLY) {
365: Set<String> props = new HashSet<String>();
366: for (Property p : getProperties()) {
367: if (p.isInserted()) {
368: props.add(p.getName());
369: }
370: }
371: return new UsageInfo(status, props);
372: }
373: }
374:
375: return new UsageInfo(status, Collections.<String> emptySet());
376: }
377:
378: /*
379: * Class UsageInfo has the usage status and it has the list of properties
380: * being initialized if the binding bean is only used to initialize them in
381: * the _init() method
382: */
383: public class UsageInfo {
384: private UsageStatus useStatus;
385: private Set<String> props;
386:
387: public UsageInfo(UsageStatus status, Set<String> props) {
388: this .useStatus = status;
389: this .props = props;
390: }
391:
392: public UsageStatus getUsageStatus() {
393: return useStatus;
394: }
395:
396: /*
397: * @return the properties initialized in _init() method, set will be empty
398: * if the UseStatus is not init_use_only
399: */
400: public Set<String> getInitializedProperties() {
401: return props;
402: }
403: }
404:
405: /*
406: * @return true if the bean is used in an EL expression inside .jsp
407: */
408: private boolean isUsedInBindingExpression() {
409: for (Bean b : unit.getBeans()) {
410: for (Property p : b.getProperties()) {
411: if (!p.isInserted()) {
412: String vbExpression = "#{"
413: + unit.getThisClass().getShortName() + "."
414: + getName() + "}";
415: String vbExpression1 = "#{"
416: + unit.getThisClass().getShortName() + "."
417: + getName() + ".";
418: String ps = p.getValueSource();
419: if (ps != null
420: && (ps.contains(vbExpression) || ps
421: .contains(vbExpression1))) {
422: return true;
423: }
424: }
425: }
426: }
427: return false;
428: }
429:
430: /*
431: * @return the list of properties that needs to be unset because of removing
432: * the binding
433: */
434: public List<String> removeBinding() {
435: List<String> propsToBeDeleted = new ArrayList<String>();
436: if (isInserted()) {
437: unit.removeBindingBean(getName());
438: for (Property p : getProperties()) {
439: if (p.isInserted()) {
440: propsToBeDeleted.add(p.getName());
441: }
442: }
443: }
444: clearBindingProperty();
445: return propsToBeDeleted;
446: }
447: }
|