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.propertyeditors;
042:
043: import com.sun.rave.designtime.faces.FacesDesignProject;
044: import java.text.MessageFormat;
045: import java.util.Comparator;
046: import java.util.HashMap;
047: import java.util.ResourceBundle;
048: import javax.faces.application.Application;
049: import javax.faces.component.UIComponent;
050: import javax.faces.context.FacesContext;
051: import javax.faces.el.MethodBinding;
052: import javax.faces.validator.DoubleRangeValidator;
053: import javax.faces.validator.LengthValidator;
054: import javax.faces.validator.LongRangeValidator;
055: import javax.faces.validator.Validator;
056: import com.sun.rave.designtime.DesignBean;
057: import com.sun.rave.designtime.DesignProperty;
058: import com.sun.rave.designtime.faces.FacesDesignContext;
059: import com.sun.rave.designtime.faces.ResolveResult;
060: import java.util.Iterator;
061: import java.util.TreeSet;
062: import javax.el.MethodExpression;
063:
064: /**
065: * An editor for properties on JSF components that take a binding to a
066: * validation method. The property may be of type {@link javax.faces.el.MethodBinding},
067: * {@link javax.el.MethodExpression}, or {@link java.lang.String}. The editor allows
068: * the user to choose from among existing validation beans in the project, validation
069: * methods defined on the current page, session or application beans. Also, a new
070: * validation bean may be created and bound to.
071: *
072: * @author gjmurphy
073: */
074: public class ValidatorPropertyEditor extends PropertyEditorBase
075: implements com.sun.rave.propertyeditors.ValidatorPropertyEditor {
076:
077: private static final Class[] VALIDATE_PARAMS = new Class[] {
078: FacesContext.class, UIComponent.class, Object.class };
079:
080: private static final ResourceBundle bundle = ResourceBundle
081: .getBundle("org.netbeans.modules.visualweb.propertyeditors.Bundle"); //NOI18N
082:
083: // Some default JSF validators, used in the event that the project context cannot
084: // be scanned for converters
085: private static final Class[] defaultFacesValidatorClasses = new Class[] {
086: DoubleRangeValidator.class, LengthValidator.class,
087: LongRangeValidator.class };
088:
089: public String[] getTags() {
090: DesignBean[] validatorBeans = this .getValidatorBeans();
091: String[] validatorLabels = this .getValidatorLabels();
092: String[] tags = new String[validatorBeans.length
093: + validatorLabels.length + 1];
094: int index = 0;
095: tags[index++] = "";
096: for (int i = 0; i < validatorBeans.length; i++) {
097: tags[index++] = validatorBeans[i].getInstanceName();
098: }
099: for (int i = 0; i < validatorLabels.length; i++) {
100: tags[index++] = validatorLabels[i];
101: }
102: return tags;
103: }
104:
105: public void setAsText(String text) throws IllegalArgumentException {
106: DesignProperty designProperty = this .getDesignProperty();
107: if (designProperty != null) {
108: FacesDesignContext context = (FacesDesignContext) designProperty
109: .getDesignBean().getDesignContext();
110: // A hack to prevent the user from overwriting a binding that is persisting
111: // the validate event handler
112: if (text == null || text.trim().length() == 0) {
113: Object value = this .getValue();
114: if (value != null
115: && (value instanceof MethodBinding || value instanceof MethodExpression)) {
116: String expressionString = null;
117: if (value instanceof MethodBinding)
118: expressionString = ((MethodBinding) value)
119: .getExpressionString();
120: else
121: expressionString = ((MethodExpression) value)
122: .getExpressionString();
123: ResolveResult result = context
124: .resolveBindingExprToBean(expressionString);
125: DesignBean designBean = result.getDesignBean();
126: if (!(designBean.getInstance() instanceof Validator))
127: return;
128: }
129: setValue(null);
130: return;
131: }
132:
133: if (text.startsWith("#{")) { //NOI18N
134: setValidatorMethodBinding(text);
135: return;
136: }
137:
138: DesignBean[] validatorBeans = this .getValidatorBeans();
139: for (int i = 0; i < validatorBeans.length; i++) {
140: if (validatorBeans[i].getInstanceName().equals(text)) {
141: setValidatorBeanBinding(validatorBeans[i]);
142: return;
143: }
144: }
145:
146: String[] validatorLabels = this .getValidatorLabels();
147: Class[] validatorClasses = this .getValidatorClasses();
148: for (int i = 0; i < validatorLabels.length; i++) {
149: if (validatorLabels[i].equals(text)) {
150: DesignBean bean = context.createBean(
151: validatorClasses[i].getName(), null, null);
152: if (bean != null) {
153: setValidatorBeanBinding(bean);
154: return;
155: }
156: }
157: }
158: }
159: }
160:
161: public String getAsText() {
162: Object value = getValue();
163: if (value == null)
164: return "";
165: if (MethodExpression.class.isAssignableFrom(value.getClass())
166: || MethodBinding.class.isAssignableFrom(value
167: .getClass())) {
168: DesignProperty designProperty = this .getDesignProperty();
169: String expression;
170: if (MethodExpression.class.isAssignableFrom(value
171: .getClass()))
172: expression = ((MethodExpression) value)
173: .getExpressionString();
174: else
175: expression = ((MethodBinding) value)
176: .getExpressionString();
177: // If the method binding expression refers to a method on a validator
178: // bean, return just the bean's instance name. Otherwise, the method
179: // binding must refer to a validate method defined on a page, request,
180: // session, or application bean. In this case return the expression.
181: if (designProperty != null) {
182: FacesDesignContext designContext = (FacesDesignContext) designProperty
183: .getDesignBean().getDesignContext();
184: ResolveResult result = designContext
185: .resolveBindingExprToBean(expression);
186: DesignBean lbean = result.getDesignBean();
187: if (lbean != null
188: && lbean.getInstance() instanceof Validator)
189: return lbean.getInstanceName();
190: return expression;
191: }
192: }
193: return value.toString();
194: }
195:
196: public String getJavaInitializationString() {
197: return "null"; //NOI18N
198: }
199:
200: private void setValidatorMethodBinding(String binding) {
201: DesignProperty designProperty = this .getDesignProperty();
202: if (designProperty == null) {
203: setValue(null);
204: } else {
205: Class propertyType = designProperty.getPropertyDescriptor()
206: .getPropertyType();
207: FacesDesignContext context = (FacesDesignContext) designProperty
208: .getDesignBean().getDesignContext();
209: Application application = context.getFacesContext()
210: .getApplication();
211: if (MethodBinding.class.isAssignableFrom(propertyType)) {
212: MethodBinding methodBinding = application
213: .createMethodBinding(binding, VALIDATE_PARAMS);
214: setValue(methodBinding);
215: } else if (MethodExpression.class
216: .isAssignableFrom(propertyType)) {
217: MethodExpression methodExpr = application
218: .getExpressionFactory().createMethodExpression(
219: context.getFacesContext()
220: .getELContext(), binding, null,
221: VALIDATE_PARAMS);
222: setValue(methodExpr);
223: } else {
224: setValue(binding);
225: }
226: }
227: }
228:
229: private void setValidatorBeanBinding(DesignBean sourceBean) {
230: DesignProperty designProperty = this .getDesignProperty();
231: if (designProperty != null) {
232: FacesDesignContext context = (FacesDesignContext) designProperty
233: .getDesignBean().getDesignContext();
234: String binding = context.getBindingExpr(sourceBean,
235: ".validate"); //NOI18N
236: setValidatorMethodBinding(binding);
237: }
238: }
239:
240: private DesignBean[] getValidatorBeans() {
241: DesignProperty designProperty = this .getDesignProperty();
242: return (designProperty == null) ? new DesignBean[0]
243: : designProperty.getDesignBean().getDesignContext()
244: .getBeansOfType(Validator.class);
245: }
246:
247: private static Comparator validatorComparator = new Comparator() {
248: public int compare(Object obj1, Object obj2) {
249: String name1 = ((Class) obj1).getName();
250: String name2 = ((Class) obj2).getName();
251: return name1
252: .substring(name1.lastIndexOf('.') + 1)
253: .compareTo(
254: name2.substring(name2.lastIndexOf('.') + 1));
255: }
256: };
257:
258: // A global map of validator IDs to classes, to avoid expensive repetitive lookups.
259: // If a new component library is imported into the IDE, any new validators will
260: // be discovered and added to the map.
261: private static HashMap validatorIdMap = new HashMap();
262:
263: private Class[] validatorClasses;
264:
265: /**
266: * Generates an array of classes for all validator components registered with the
267: * design-time JSF application.
268: */
269: protected Class[] getValidatorClasses() {
270: if (validatorClasses != null)
271: return validatorClasses;
272: DesignProperty designProperty = this .getDesignProperty();
273: if (designProperty == null)
274: return defaultFacesValidatorClasses;
275: FacesDesignProject facesDesignProject = (FacesDesignProject) designProperty
276: .getDesignBean().getDesignContext().getProject();
277: ClassLoader oldContextClassLoader = Thread.currentThread()
278: .getContextClassLoader();
279: try {
280: Thread.currentThread().setContextClassLoader(
281: facesDesignProject.getContextClassLoader());
282: FacesContext facesContext = ((FacesDesignContext) designProperty
283: .getDesignBean().getDesignContext())
284: .getFacesContext();
285: Application application = facesContext.getApplication();
286: Iterator iter = application.getValidatorIds();
287: TreeSet set = new TreeSet(validatorComparator);
288: while (iter.hasNext()) {
289: String id = (String) iter.next();
290: if (!validatorIdMap.containsKey(id)) {
291: Validator validator = application
292: .createValidator(id);
293: validatorIdMap.put(id, validator.getClass());
294: }
295: set.add(validatorIdMap.get(id));
296: }
297: validatorClasses = new Class[set.size()];
298: iter = set.iterator();
299: for (int i = 0; i < validatorClasses.length; i++)
300: validatorClasses[i] = (Class) iter.next();
301: } finally {
302: Thread.currentThread().setContextClassLoader(
303: oldContextClassLoader);
304: }
305: return validatorClasses;
306: }
307:
308: private String[] validatorLabels;
309:
310: /**
311: * Generates an array of display labels for all converter classes.
312: */
313: protected String[] getValidatorLabels() {
314: if (validatorLabels != null)
315: return validatorLabels;
316: Class[] validatorClasses = getValidatorClasses();
317: validatorLabels = new String[validatorClasses.length];
318: MessageFormat labelFormat = new MessageFormat(bundle
319: .getString("ValidatorPropertyEditor.newValidatorLabel")); //NOI18N
320: Object[] args = new Object[1];
321: for (int i = 0; i < validatorClasses.length; i++) {
322: String name = validatorClasses[i].getName();
323: args[0] = name.substring(name.lastIndexOf('.') + 1);
324: validatorLabels[i] = labelFormat.format(args);
325: }
326: return validatorLabels;
327: }
328:
329: }
|