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.web.ui.dt.component;
042:
043: import com.sun.rave.designtime.DesignBean;
044: import com.sun.rave.designtime.DesignProperty;
045: import com.sun.rave.designtime.Result;
046: import com.sun.rave.designtime.faces.FacesDesignContext;
047: import com.sun.rave.designtime.faces.FacesDesignProperty;
048: import com.sun.rave.designtime.faces.ResolveResult;
049: import com.sun.rave.web.ui.component.Selector;
050: import com.sun.rave.web.ui.model.DefaultOptionsList;
051: import com.sun.rave.web.ui.model.OptionsList;
052: import java.beans.PropertyDescriptor;
053: import java.util.regex.Pattern;
054: import javax.faces.el.ValueBinding;
055:
056: /** DesignInfo class for components that extend the {@link
057: * org.netbeans.modules.visualweb.web.ui.dt.component.Selector} component. The following behaviors are
058: * implemented:
059: * <ul>
060: * <li>When a new selector-based component is created, a default list of
061: * options is created, and bound to the component's <code>items</code>
062: * property.</li>
063: * </ul>
064: *
065: * @author gjmurphy
066: */
067: public class SelectorDesignInfo extends EditableValueHolderDesignInfo {
068:
069: static final String ITEMS = "items"; //NOI18N
070: static final String MULTIPLE = "multiple"; //NOI18N
071: static final String CONVERTER = "converter"; //NOI18N
072: static final String SELECTED_VALUE = "selectedValue"; //NOI18N
073: static final String ID = "id"; //NOI18N
074:
075: public SelectorDesignInfo(Class clazz) {
076: super (clazz);
077: }
078:
079: /** When a new Selector-based component is dropped, create a default
080: * list of options and bind if to this component's <code>items</code> and
081: * <code>selected</code> properties.
082: *
083: * @param bean <code>DesignBean</code> for the newly created instance
084: */
085: public Result beanCreatedSetup(DesignBean bean) {
086: FacesDesignContext context = (FacesDesignContext) bean
087: .getDesignContext();
088: Class optionsListClass = getOptionsListClass();
089: if (context.canCreateBean(optionsListClass.getName(), null,
090: null)) {
091: DesignBean options = context.createBean(optionsListClass
092: .getName(), null, null);
093: options.setInstanceName(getOptionsListName(bean), true);
094: bean.getProperty(ITEMS).setValueSource(
095: context.getBindingExpr(options, ".options")); //NOI18N
096: }
097: return Result.SUCCESS;
098: }
099:
100: /**
101: * When a Selector-based component is deleted, check for the existence of a
102: * default list of options, and delete it if present.
103: */
104: public Result beanDeletedCleanup(DesignBean bean) {
105: super .beanDeletedCleanup(bean);
106: // If bound to a default options bean, and no other selector components
107: // are bound to it, delete it
108: DesignBean options = getOptionsListBean(bean);
109: if (options != null) {
110: FacesDesignProperty itemsProperty = (FacesDesignProperty) bean
111: .getProperty(ITEMS);
112: String oldExpression = itemsProperty.getValueBinding()
113: .getExpressionString();
114: itemsProperty.unset();
115: DesignBean[] beans = bean.getDesignContext()
116: .getBeansOfType(Selector.class);
117: int referenceCount = 0;
118: for (int i = 0; i < beans.length; i++) {
119: DesignProperty p = beans[i].getProperty(ITEMS);
120: if (p != null && p instanceof FacesDesignProperty) {
121: ValueBinding valueBinding = ((FacesDesignProperty) p)
122: .getValueBinding();
123: if (valueBinding != null) {
124: String expression = valueBinding
125: .getExpressionString();
126: if (oldExpression.equals(expression))
127: referenceCount++;
128: }
129: }
130: }
131: if (referenceCount == 0)
132: bean.getDesignContext().deleteBean(options);
133: }
134: deleteConverter(bean);
135: return Result.SUCCESS;
136: }
137:
138: public Result beanPastedSetup(DesignBean bean) {
139: FacesDesignContext context = (FacesDesignContext) bean
140: .getDesignContext();
141: // If selector cut or copied and then pasted, and it looks like the
142: // component was previously bound to a default options bean, create a
143: // new bean for the pasted component. If the paste operation follows a
144: // copy operation, then copy the previous default option's properties
145: DesignProperty itemsProperty = bean.getProperty(ITEMS);
146: if (itemsProperty != null
147: && itemsProperty.getValueSource() != null
148: && itemsProperty.getValueSource().indexOf(
149: "DefaultOptions") >= 0) {
150: DesignBean options = context.createBean(
151: getOptionsListClass().getName(), null, null);
152: options.setInstanceName(getOptionsListName(bean), true);
153: itemsProperty.setValueSource(context.getBindingExpr(
154: options, ".options")); // NOI18N
155: }
156: return Result.SUCCESS;
157: }
158:
159: protected DesignProperty getDefaultBindingProperty(
160: DesignBean targetBean) {
161: return targetBean.getProperty(ITEMS); //NOI18N
162: }
163:
164: private static Pattern fieldKeysPattern = Pattern
165: .compile("options\\s*\\[\\s*'\\s*([\\w.]+)\\s*(,\\s*([\\w.]+)\\s*)?'\\s*\\]"); //NOI18N
166:
167: /**
168: * When the <code>items</code> property is changed, if previous value was a
169: * binding to the default <code>OptionsList</code> for this component, then
170: * delete the options list if present.
171: */
172: public void propertyChanged(DesignProperty property, Object oldValue) {
173: super .propertyChanged(property, oldValue);
174: PropertyDescriptor descriptor = property
175: .getPropertyDescriptor();
176: DesignBean selectorBean = property.getDesignBean();
177: FacesDesignContext context = (FacesDesignContext) selectorBean
178: .getDesignContext();
179: // If instance name is changed: if the component is bound to a default
180: // options list, update the name of the options list to reflect the new
181: // instance name; if the component is bound to a converter, update the
182: // name of the converter to reflect the new instance name
183: if (descriptor.getName().equals(ID)) {
184: DesignBean optionsBean = getOptionsListBean(selectorBean);
185: if (optionsBean != null) {
186: optionsBean
187: .setInstanceName(getOptionsListName(selectorBean));
188: selectorBean.getProperty(ITEMS)
189: .setValueSource(
190: context.getBindingExpr(optionsBean,
191: ".options")); //NOI18N
192: }
193: DesignBean converterBean = getConverterBean(selectorBean);
194: if (converterBean != null) {
195: converterBean
196: .setInstanceName(getConverterName(selectorBean));
197: selectorBean.getProperty(CONVERTER).setValue(
198: converterBean.getInstance());
199: }
200: } else if (descriptor.getName().equals(ITEMS)) {
201: // If previous value was a value binding to a default options list, and new
202: // value is not a value binding to the same options list, then deleat the
203: // default options list
204: if (oldValue != null
205: && ValueBinding.class.isAssignableFrom(oldValue
206: .getClass())) {
207: ValueBinding valueBinding = ((FacesDesignProperty) property)
208: .getValueBinding();
209: String oldExpression = ((ValueBinding) oldValue)
210: .getExpressionString();
211: if (((FacesDesignProperty) property).isBound()
212: && !oldExpression.equals(valueBinding
213: .getExpressionString())) {
214: DesignBean optionsBean = getOptionsListBean(
215: context, oldExpression);
216: if (optionsBean != null) {
217: // Make sure no other components are using the default options
218: DesignBean[] beans = context
219: .getBeansOfType(Selector.class);
220: int referenceCount = 0;
221: for (int i = 0; i < beans.length; i++) {
222: DesignProperty p = beans[i]
223: .getProperty(ITEMS);
224: if (p != null
225: && p instanceof FacesDesignProperty) {
226: String expression = ((FacesDesignProperty) p)
227: .getValueBinding()
228: .getExpressionString();
229: if (oldExpression.equals(expression))
230: referenceCount++;
231: }
232: }
233: if (referenceCount == 0)
234: context.deleteBean(optionsBean);
235: }
236: }
237: }
238: // If new value is a value binding to options supplied by a dataprovider,
239: // create or modify any converters needed
240: modifyConverter(property);
241: }
242: }
243:
244: /**
245: * Returns a class to instantiate for the default options bean. Must be
246: * <code>OptionsList</code> or a subclass thereof.
247: */
248: protected Class getOptionsListClass() {
249: return DefaultOptionsList.class;
250: }
251:
252: /**
253: * Returns the name of the default options bean.
254: */
255: protected static String getOptionsListName(DesignBean selectorBean) {
256: return selectorBean.getInstanceName() + "DefaultOptions"; //NOI18N
257: }
258:
259: /**
260: * If the selector component for the bean specified is bound to an options
261: * list, returns the design bean for the options list. Otherwise returns
262: * null.
263: */
264: protected static DesignBean getOptionsListBean(
265: DesignBean selectorBean) {
266: FacesDesignContext context = (FacesDesignContext) selectorBean
267: .getDesignContext();
268: FacesDesignProperty itemsProperty = (FacesDesignProperty) selectorBean
269: .getProperty(ITEMS);
270: if (itemsProperty == null || !itemsProperty.isBound())
271: return null;
272: String expression = itemsProperty.getValueBinding()
273: .getExpressionString();
274: return getOptionsListBean(context, expression);
275: }
276:
277: protected static DesignBean getOptionsListBean(
278: FacesDesignContext context, String expression) {
279: ResolveResult resolveResult = context
280: .resolveBindingExprToBean(expression);
281: if (resolveResult == null
282: || resolveResult.getDesignBean() == null)
283: return null;
284: DesignBean itemsBean = resolveResult.getDesignBean();
285: if (OptionsList.class.isAssignableFrom(itemsBean.getInstance()
286: .getClass()))
287: return itemsBean;
288: return null;
289: }
290:
291: }
|