0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.form;
0043:
0044: import com.sun.source.tree.*;
0045: import com.sun.source.util.TreePath;
0046: import com.sun.source.util.Trees;
0047: import java.lang.ref.*;
0048: import java.lang.reflect.*;
0049: import java.util.*;
0050: import java.beans.*;
0051: import java.io.IOException;
0052: import java.util.logging.Level;
0053: import java.util.logging.Logger;
0054: import javax.lang.model.element.Element;
0055: import javax.lang.model.element.ElementKind;
0056: import javax.lang.model.element.TypeElement;
0057: import javax.lang.model.element.TypeParameterElement;
0058: import javax.lang.model.type.TypeKind;
0059: import javax.swing.JList;
0060: import javax.swing.JTable;
0061: import javax.swing.JComboBox;
0062: import javax.swing.JSlider;
0063: import javax.swing.JSpinner;
0064: import javax.swing.text.JTextComponent;
0065: import org.jdesktop.beansbinding.*;
0066: import org.jdesktop.beansbinding.ext.BeanAdapterFactory;
0067: import org.jdesktop.swingbinding.*;
0068: import org.netbeans.api.java.classpath.ClassPath;
0069: import org.netbeans.api.java.source.CancellableTask;
0070: import org.netbeans.api.java.source.CompilationController;
0071: import org.netbeans.api.java.source.JavaSource;
0072: import org.netbeans.modules.form.FormUtils.TypeHelper;
0073: import org.netbeans.modules.form.project.ClassPathUtils;
0074: import org.openide.filesystems.FileObject;
0075: import org.openide.nodes.Node;
0076:
0077: /**
0078: * Design support for beans binding.
0079: *
0080: * @author Jan Stola, Tomas Pavek
0081: */
0082: public class BindingDesignSupport {
0083: /** Form model. */
0084: private FormModel formModel;
0085:
0086: /** Realizations of bindings among replicated components. */
0087: private Map<MetaBinding, List<Binding>> bindingsMap = new HashMap<MetaBinding, List<Binding>>();
0088: /** Realizations of bindings among metacomponents. */
0089: private Map<MetaBinding, Binding> modelBindings = new HashMap<MetaBinding, Binding>();
0090: /** Binding to BindingGroup mapping. */
0091: private Map<Binding, BindingGroup> bindingToGroup = new HashMap<Binding, BindingGroup>();
0092: /** Binding group for reference instances in metacomponents. */
0093: private BindingGroup bindingGroup;
0094:
0095: /**
0096: * Create binding design support for the given form model.
0097: *
0098: * @param model form model to create the binding support for.
0099: */
0100: public BindingDesignSupport(FormModel model) {
0101: formModel = model;
0102:
0103: bindingGroup = new BindingGroup();
0104: bindingGroup.bind();
0105:
0106: formModel.addFormModelListener(new ModelListener());
0107: }
0108:
0109: /**
0110: * Changes the binding between two components (affects only replicated components).
0111: *
0112: * @param oldBinding the old definition of the binding.
0113: * @param newBinding the new definition of the binding.
0114: */
0115: private void changeBinding(MetaBinding oldBinding,
0116: MetaBinding newBinding) {
0117: if (oldBinding != null) {
0118: removeBindings(oldBinding);
0119: }
0120: // non-model bindings are added from VisualReplicator
0121: }
0122:
0123: /**
0124: * Changes the binding between two components (affects only reference instances in the model).
0125: *
0126: * @param oldBinding the old definition of the binding.
0127: * @param newBinding the new definition of the binding.
0128: */
0129: public void changeBindingInModel(MetaBinding oldBinding,
0130: MetaBinding newBinding) {
0131: if (oldBinding != null) {
0132: removeBindingInModel(oldBinding);
0133: }
0134: if (newBinding != null) {
0135: addBindingInModel(newBinding);
0136: }
0137: }
0138:
0139: /**
0140: * Turns given string (usually dot-separated path) into EL expression
0141: * by adding <code>${</code> and <code>}</code> braces.
0142: *
0143: * @param path string to transform into EL expression.
0144: * @return EL expression corresponding to the given path.
0145: */
0146: public static String elWrap(String path) {
0147: return (path == null) ? null : "${" + path + "}"; // NOI18N
0148: }
0149:
0150: /**
0151: * Determines whether the given string is simple EL expression.
0152: *
0153: * @param expression string to check.
0154: * @return <code>true</code> if the given string starts with
0155: * <code>${</code> and ends with <code>}</code>, returns <code>false</code>
0156: * otherwise.
0157: */
0158: public static boolean isSimpleExpression(String expression) {
0159: return (expression.startsWith("${") && expression.endsWith("}")); // NOI18N
0160: }
0161:
0162: /**
0163: * Removes <code>${</code> and <code>}</code> braces from a simple
0164: * EL expression. Non-simple expressions are left untouched.
0165: *
0166: * @param expression expression to unwrap.
0167: * @return unwrapped expression or the given string
0168: * (if it is not a simple EL expression).
0169: */
0170: public static String unwrapSimpleExpression(String expression) {
0171: if (isSimpleExpression(expression)) {
0172: expression = expression.substring(2,
0173: expression.length() - 1);
0174: }
0175: return expression;
0176: }
0177:
0178: private static boolean hasRelativeType(Class clazz, String property) {
0179: return ("elements".equals(property) // NOI18N
0180: // selectedElement(_...), selectedElements(_...)
0181: || property.startsWith("selectedElement")) // NOI18N
0182: && (javax.swing.JTable.class.isAssignableFrom(clazz)
0183: || javax.swing.JList.class
0184: .isAssignableFrom(clazz) || javax.swing.JComboBox.class
0185: .isAssignableFrom(clazz));
0186: }
0187:
0188: // Used to determine binding properties only
0189: List<BindingDescriptor>[] getBindingDescriptors(
0190: RADComponent component) {
0191: BeanDescriptor beanDescriptor = component.getBeanInfo()
0192: .getBeanDescriptor();
0193: List<BindingDescriptor>[] descs = getBindingDescriptors(null,
0194: beanDescriptor, false);
0195: Class<?> beanClass = component.getBeanClass();
0196: if (JTextComponent.class.isAssignableFrom(beanClass)) {
0197: // get rid of text_... descriptors
0198: descs[0] = filterDescriptors(descs[0], "text_"); // NOI18N
0199: } else if (JTable.class.isAssignableFrom(beanClass)
0200: || JList.class.isAssignableFrom(beanClass)
0201: || JComboBox.class.isAssignableFrom(beanClass)) {
0202: // get rid of selectedElement(s)_... descriptors
0203: descs[0] = filterDescriptors(descs[0], "selectedElement_"); // NOI18N
0204: descs[0] = filterDescriptors(descs[0], "selectedElements_"); // NOI18N
0205: // add elements descriptor
0206: BindingDescriptor desc = new BindingDescriptor("elements",
0207: List.class); // NOI18N
0208: descs[0].add(0, desc);
0209: } else if (JSlider.class.isAssignableFrom(beanClass)) {
0210: // get rid of value_... descriptor
0211: descs[0] = filterDescriptors(descs[0], "value_"); // NOI18N
0212: }
0213: return descs;
0214: }
0215:
0216: private List<BindingDescriptor> filterDescriptors(
0217: List<BindingDescriptor> descs, String forbiddenPrefix) {
0218: List<BindingDescriptor> filtered = new LinkedList<BindingDescriptor>();
0219: for (BindingDescriptor bd : descs) {
0220: if (!bd.getPath().startsWith(forbiddenPrefix)) { // NOI18N
0221: filtered.add(bd);
0222: }
0223: }
0224: return filtered;
0225: }
0226:
0227: private List<PropertyDescriptor> getSpecialBindingDescriptors(
0228: Class clazz) {
0229: List<PropertyDescriptor> descs = BeanAdapterFactory
0230: .getAdapterPropertyDescriptors(clazz);
0231: try {
0232: if (JComboBox.class.isAssignableFrom(clazz)) {
0233: PropertyDescriptor desc = new PropertyDescriptor(
0234: "selectedItem", JComboBox.class); // NOI18N
0235: descs.add(desc);
0236: } else if (JSpinner.class.isAssignableFrom(clazz)) {
0237: PropertyDescriptor desc = new PropertyDescriptor(
0238: "value", JSpinner.class); // NOI18N
0239: descs.add(desc);
0240: }
0241: } catch (Exception ex) {
0242: Logger.getLogger(getClass().getName()).log(Level.INFO,
0243: ex.getMessage(), ex);
0244: }
0245: return descs;
0246: }
0247:
0248: private List<BindingDescriptor>[] getBindingDescriptors(
0249: TypeHelper type, BeanDescriptor beanDescriptor,
0250: boolean includeReadOnly) {
0251: Class<?> beanClass = beanDescriptor.getBeanClass();
0252: List<BindingDescriptor> bindingList = new LinkedList<BindingDescriptor>();
0253: List<BindingDescriptor> prefList = new LinkedList<BindingDescriptor>();
0254: List<BindingDescriptor> observableList = new LinkedList<BindingDescriptor>();
0255: List<BindingDescriptor> nonObservableList = new LinkedList<BindingDescriptor>();
0256: List<BindingDescriptor> list;
0257: Object[] propsCats = FormUtils.getPropertiesCategoryClsf(
0258: beanClass, beanDescriptor);
0259: PropertyDescriptor[] pds;
0260: try {
0261: pds = FormUtils.getBeanInfo(beanClass)
0262: .getPropertyDescriptors();
0263: } catch (Exception ex) {
0264: Logger.getLogger(getClass().getName()).log(Level.INFO,
0265: ex.getMessage(), ex);
0266: pds = new PropertyDescriptor[0];
0267: }
0268: List<PropertyDescriptor> specialPds = getSpecialBindingDescriptors(beanClass);
0269: Map<String, PropertyDescriptor> pathToDesc = new HashMap<String, PropertyDescriptor>();
0270: for (PropertyDescriptor pd : pds) {
0271: pathToDesc.put(pd.getName(), pd);
0272: }
0273: for (PropertyDescriptor pd : specialPds) {
0274: if (pathToDesc.get(pd.getName()) != null) {
0275: pathToDesc.remove(pd.getName());
0276: }
0277: }
0278: List<PropertyDescriptor> allPds = new LinkedList<PropertyDescriptor>(
0279: specialPds);
0280: allPds.addAll(pathToDesc.values());
0281: int count = 0;
0282: for (PropertyDescriptor pd : allPds) {
0283: if (count++ < specialPds.size()) {
0284: list = bindingList;
0285: } else {
0286: if (!includeReadOnly && (pd.getWriteMethod() == null)) {
0287: continue;
0288: }
0289: Object propCat = FormUtils.getPropertyCategory(pd,
0290: propsCats);
0291: if (propCat == FormUtils.PROP_HIDDEN) {
0292: // hidden property => hide also the binding property
0293: continue;
0294: } else {
0295: if (pd.isBound()) {
0296: // observable property
0297: if (propCat == FormUtils.PROP_PREFERRED) {
0298: list = prefList;
0299: } else {
0300: list = observableList;
0301: }
0302: } else {
0303: // non-observable property
0304: list = nonObservableList;
0305: }
0306: }
0307: }
0308:
0309: Method method = pd.getReadMethod();
0310: if ((method != null)
0311: && ("getClass".equals(method.getName())))
0312: continue; // NOI18N
0313: Type retType = (method == null) ? pd.getPropertyType()
0314: : method.getGenericReturnType();
0315: if (retType == null)
0316: continue;
0317: BindingDescriptor bd;
0318: if (type == null) {
0319: bd = new BindingDescriptor(pd.getName(), retType);
0320: } else {
0321: TypeHelper t = new TypeHelper(retType, type
0322: .getActualTypeArgs()).normalize();
0323: bd = new BindingDescriptor(pd.getName(), t);
0324: }
0325: bd.setDisplayName(pd.getDisplayName());
0326: bd.setShortDescription(pd.getShortDescription());
0327:
0328: if (hasRelativeType(beanClass, bd.getPath())) {
0329: bd.markTypeAsRelative();
0330: }
0331:
0332: list.add(bd);
0333: }
0334:
0335: if (bindingList.isEmpty()) {
0336: bindingList = prefList;
0337: } else {
0338: observableList.addAll(prefList);
0339: }
0340: Comparator<BindingDescriptor> bdComparator = new Comparator<BindingDescriptor>() {
0341: public int compare(BindingDescriptor o1,
0342: BindingDescriptor o2) {
0343: String path1 = o1.getPath();
0344: String path2 = o2.getPath();
0345: return path1.compareToIgnoreCase(path2);
0346: }
0347: };
0348: Collections.sort(bindingList, bdComparator);
0349: Collections.sort(observableList, bdComparator);
0350: Collections.sort(nonObservableList, bdComparator);
0351:
0352: return new List[] { bindingList, observableList,
0353: nonObservableList };
0354: }
0355:
0356: public List<BindingDescriptor> getAllBindingDescriptors(
0357: TypeHelper type) {
0358: List<BindingDescriptor>[] descs = getBindingDescriptors(type);
0359: List<BindingDescriptor> list = new LinkedList<BindingDescriptor>();
0360: for (int i = 0; i < descs.length; i++) {
0361: list.addAll(descs[i]);
0362: }
0363: return list;
0364: }
0365:
0366: /**
0367: * Returns possible bindings for the given type.
0368: *
0369: * @param type type whose possible bindings should be returned.
0370: * @return list of <code>BindingDescriptor</code>s describing possible bindings.
0371: */
0372: @SuppressWarnings("unchecked")
0373: // generic array creation NOI18N
0374: public List<BindingDescriptor>[] getBindingDescriptors(
0375: TypeHelper type) {
0376: List<BindingDescriptor> typesFromSource = Collections
0377: .emptyList();
0378: Class binarySuperClass = null;
0379: if (type.getType() == null) {
0380: FileObject fileInProject = FormEditor.getFormDataObject(
0381: formModel).getPrimaryFile();
0382: ClassPath cp = ClassPath.getClassPath(fileInProject,
0383: ClassPath.SOURCE);
0384: final List<BindingDescriptor> types = new LinkedList<BindingDescriptor>();
0385: final String[] super Class = new String[1];
0386: super Class[0] = type.getName();
0387: do {
0388: String typeName = super Class[0];
0389: final String resourceName = typeName.replace('.', '/')
0390: + ".java"; // NOI18N
0391: int lastDot = typeName.lastIndexOf('.');
0392: lastDot = (lastDot == -1) ? 0 : lastDot;
0393: final String simpleTypeName = typeName
0394: .substring(lastDot + 1);
0395: FileObject fob = cp.findResource(resourceName);
0396: if (fob == null) {
0397: try {
0398: binarySuperClass = ClassPathUtils.loadClass(
0399: typeName, fileInProject);
0400: } catch (ClassNotFoundException cnfex) {
0401: }
0402: break;
0403: }
0404: JavaSource source = JavaSource.forFileObject(fob);
0405: try {
0406: source
0407: .runUserActionTask(
0408: new CancellableTask<CompilationController>() {
0409: public void run(
0410: CompilationController cc)
0411: throws Exception {
0412: cc
0413: .toPhase(JavaSource.Phase.RESOLVED);
0414: CompilationUnitTree cu = cc
0415: .getCompilationUnit();
0416: ClassTree clazz = null;
0417: for (Tree typeDecl : cu
0418: .getTypeDecls()) {
0419: if (Tree.Kind.CLASS == typeDecl
0420: .getKind()) {
0421: ClassTree candidate = (ClassTree) typeDecl;
0422: if (candidate
0423: .getSimpleName()
0424: .toString()
0425: .equals(
0426: simpleTypeName)) {
0427: clazz = candidate;
0428: break;
0429: }
0430: }
0431: }
0432: if (clazz == null) { // issue 118690
0433: // should not happen
0434: Logger
0435: .getLogger(
0436: getClass()
0437: .getName())
0438: .log(
0439: Level.INFO,
0440: "ClassTree not found in "
0441: + resourceName); // NOI18N
0442: super Class[0] = Object.class
0443: .getName();
0444: return;
0445: }
0446: for (Tree clMember : clazz
0447: .getMembers()) {
0448: if (clMember.getKind() == Tree.Kind.METHOD) {
0449: MethodTree method = (MethodTree) clMember;
0450: if (method
0451: .getParameters()
0452: .size() != 0)
0453: continue;
0454: Set<javax.lang.model.element.Modifier> modifiers = method
0455: .getModifiers()
0456: .getFlags();
0457: if (modifiers
0458: .contains(javax.lang.model.element.Modifier.STATIC)
0459: || !modifiers
0460: .contains(javax.lang.model.element.Modifier.PUBLIC)) {
0461: continue;
0462: }
0463: String methodName = method
0464: .getName()
0465: .toString();
0466: Tree returnType = method
0467: .getReturnType();
0468:
0469: String propName;
0470: if (methodName
0471: .startsWith("get")) { // NOI18N
0472: propName = methodName
0473: .substring(3);
0474: } else if (methodName
0475: .startsWith("is")) { // NOI18N
0476: if ((returnType
0477: .getKind() == Tree.Kind.PRIMITIVE_TYPE)
0478: && (((PrimitiveTypeTree) returnType)
0479: .getPrimitiveTypeKind() == TypeKind.BOOLEAN)) {
0480: propName = methodName
0481: .substring(2);
0482: } else {
0483: continue;
0484: }
0485: } else {
0486: continue;
0487: }
0488: if (propName
0489: .length() == 0)
0490: continue;
0491: if ((propName
0492: .length() == 1)
0493: || (Character
0494: .isLowerCase(propName
0495: .charAt(1)))) {
0496: propName = Character
0497: .toLowerCase(propName
0498: .charAt(0))
0499: + propName
0500: .substring(1);
0501: }
0502:
0503: TypeHelper type;
0504: if (returnType
0505: .getKind() == Tree.Kind.PRIMITIVE_TYPE) {
0506: PrimitiveTypeTree ptree = (PrimitiveTypeTree) returnType;
0507: if (ptree
0508: .getPrimitiveTypeKind() == TypeKind.VOID) {
0509: continue; // void return type
0510: }
0511: type = new TypeHelper(
0512: ptree
0513: .toString());
0514: } else {
0515: type = treeToType(
0516: cc,
0517: returnType,
0518: formModel);
0519: }
0520: types
0521: .add(
0522: 0,
0523: new BindingDescriptor(
0524: propName,
0525: type));
0526: }
0527: }
0528: Tree super Tree = clazz
0529: .getExtendsClause();
0530: TypeHelper type = treeToType(
0531: cc, super Tree,
0532: formModel);
0533: String typeName = type
0534: .getName();
0535: super Class[0] = (typeName == null) ? FormUtils
0536: .typeToClass(type)
0537: .getName()
0538: : typeName;
0539: }
0540:
0541: public void cancel() {
0542: }
0543:
0544: }, true);
0545: } catch (IOException ioex) {
0546: Logger.getLogger(getClass().getName()).log(
0547: Level.INFO, ioex.getMessage(), ioex);
0548: }
0549: } while (!Object.class.equals(super Class[0]));
0550: typesFromSource = types;
0551: }
0552: List<BindingDescriptor>[] list = new List[] {
0553: Collections.emptyList(), typesFromSource,
0554: Collections.emptyList() };
0555: Class clazz = (type.getType() == null) ? binarySuperClass
0556: : FormUtils.typeToClass(type);
0557: if ((clazz != null)
0558: && !clazz.getName().startsWith("java.lang.") // NOI18N
0559: && !Collection.class.isAssignableFrom(clazz)
0560: && !clazz.isArray()) {
0561: try {
0562: BeanInfo beanInfo = FormUtils.getBeanInfo(clazz);
0563: List<BindingDescriptor>[] typesFromBinary = getBindingDescriptors(
0564: type, beanInfo.getBeanDescriptor(), true);
0565: Map<String, BindingDescriptor>[] maps = new Map[3];
0566: for (int i = 0; i < 3; i++) {
0567: maps[i] = listToMap(typesFromBinary[i]);
0568: }
0569: for (BindingDescriptor descriptor : typesFromSource) {
0570: String path = descriptor.getPath();
0571: int i;
0572: for (i = 0; i < 3; i++) {
0573: if (maps[i].containsKey(path))
0574: break;
0575: }
0576: if (i == 3) {
0577: i = 1; // put into observablle properties by default
0578: }
0579: maps[i].put(path, descriptor);
0580: }
0581: for (int i = 0; i < 3; i++) {
0582: list[i] = new LinkedList<BindingDescriptor>(maps[i]
0583: .values());
0584: }
0585: } catch (Exception ex) {
0586: Logger.getLogger(getClass().getName()).log(Level.INFO,
0587: ex.getMessage(), ex);
0588: }
0589: }
0590: return list;
0591: }
0592:
0593: private static TypeHelper treeToType(CompilationController cc,
0594: Tree tree, FormModel model) {
0595: String typeName = Object.class.getName();
0596: Map<String, TypeHelper> map = null;
0597: if (tree != null) {
0598: CompilationUnitTree cu = cc.getCompilationUnit();
0599: Trees trees = cc.getTrees();
0600: if (tree.getKind() == Tree.Kind.EXTENDS_WILDCARD) {
0601: tree = ((WildcardTree) tree).getBound();
0602: }
0603: TreePath path = trees.getPath(cu, tree);
0604: Element el = trees.getElement(path);
0605: if ((el != null)
0606: && ((el.getKind() == ElementKind.CLASS) || (el
0607: .getKind() == ElementKind.INTERFACE))) {
0608: TypeElement tel = (TypeElement) el;
0609: typeName = tel.getQualifiedName().toString();
0610: if (tree.getKind() == Tree.Kind.PARAMETERIZED_TYPE) {
0611: List<? extends Tree> params = ((ParameterizedTypeTree) tree)
0612: .getTypeArguments();
0613: List<? extends TypeParameterElement> elems = tel
0614: .getTypeParameters();
0615: map = new HashMap<String, TypeHelper>();
0616: for (int i = 0; i < params.size()
0617: && i < elems.size(); i++) {
0618: Tree param = params.get(0);
0619: TypeHelper paramType = treeToType(cc, param,
0620: model);
0621: TypeParameterElement elem = elems.get(0);
0622: map.put(elem.toString(), paramType);
0623: }
0624: }
0625: }
0626: }
0627: TypeHelper type = new TypeHelper(typeName, map);
0628: if (typeName.indexOf('.') != -1) {
0629: try {
0630: Class clazz = FormUtils.loadClass(typeName, model);
0631: type = new TypeHelper(clazz, map);
0632: } catch (ClassNotFoundException cnfex) {
0633: // not compiled - use just the name
0634: }
0635: }
0636: return type;
0637: }
0638:
0639: private static Map<String, BindingDescriptor> listToMap(
0640: List<BindingDescriptor> list) {
0641: Map<String, BindingDescriptor> map = new TreeMap<String, BindingDescriptor>();
0642: for (BindingDescriptor descriptor : list) {
0643: String path = descriptor.getPath();
0644: map.put(path, descriptor);
0645: }
0646: return map;
0647: }
0648:
0649: /**
0650: * Determines type of RAD component.
0651: *
0652: * @param comp RAD component whose type should be returned.
0653: * @return <code>TypeHelper</code> that corresponds to the type of the given component.
0654: */
0655: static TypeHelper determineType(RADComponent comp) {
0656: TypeHelper type;
0657: if (comp.getFormModel().getTopRADComponent() == comp) {
0658: FileObject fob = FormEditor.getFormDataObject(
0659: comp.getFormModel()).getPrimaryFile();
0660: ClassPath cp = ClassPath
0661: .getClassPath(fob, ClassPath.SOURCE);
0662: String className = cp.getResourceName(fob, '.', false);
0663: type = new TypeHelper(className);
0664: } else {
0665: Type t = null;
0666: Map<String, TypeHelper> newMap = null;
0667: Class clazz = comp.getBeanClass();
0668: t = clazz;
0669: if (clazz.getTypeParameters().length == 1) {
0670: try {
0671: TypeHelper elemType = determineTypeParameter(comp);
0672: if (elemType != null) {
0673: newMap = new HashMap<String, TypeHelper>();
0674: newMap.put(clazz.getTypeParameters()[0]
0675: .getName(), elemType);
0676: }
0677: } catch (Exception ex) {
0678: Logger.getLogger(
0679: BindingDesignSupport.class.getName()).log(
0680: Level.INFO, ex.getMessage(), ex);
0681: }
0682: }
0683: type = new TypeHelper(t, newMap);
0684: }
0685: return type;
0686: }
0687:
0688: static TypeHelper determineTypeParameter(final RADComponent comp) {
0689: FileObject fob = FormEditor.getFormDataObject(
0690: comp.getFormModel()).getPrimaryFile();
0691: JavaSource source = JavaSource.forFileObject(fob);
0692: final String varName = comp.getName();
0693: final TypeHelper[] result = new TypeHelper[1];
0694: try {
0695: source.runUserActionTask(
0696: new CancellableTask<CompilationController>() {
0697: public void run(CompilationController cc)
0698: throws Exception {
0699: cc.toPhase(JavaSource.Phase.RESOLVED);
0700: CompilationUnitTree cu = cc
0701: .getCompilationUnit();
0702: ClassTree clazz = null;
0703: for (Tree typeDecl : cu.getTypeDecls()) {
0704: if (Tree.Kind.CLASS == typeDecl
0705: .getKind()) {
0706: clazz = (ClassTree) typeDecl;
0707: break;
0708: }
0709: }
0710: Node.Property prop = comp
0711: .getSyntheticProperty("useLocalVariable"); // NOI18N
0712: Object value = prop.getValue();
0713: VariableTree variable = null;
0714: if (Boolean.TRUE.equals(value)) {
0715: // local variable in initComponents()
0716: for (Tree clMember : clazz.getMembers()) {
0717: if (clMember.getKind() == Tree.Kind.METHOD) {
0718: MethodTree method = (MethodTree) clMember;
0719: String methodName = method
0720: .getName().toString();
0721: if ("initComponents"
0722: .equals(methodName)) { // NOI18N
0723: for (StatementTree statement : method
0724: .getBody()
0725: .getStatements()) {
0726: if (statement.getKind() == Tree.Kind.VARIABLE) {
0727: VariableTree var = (VariableTree) statement;
0728: if (varName
0729: .equals(var
0730: .getName()
0731: .toString())) {
0732: variable = var;
0733: }
0734: }
0735: }
0736: }
0737: }
0738: }
0739: } else {
0740: // fields in class
0741: for (Tree clMember : clazz.getMembers()) {
0742: if (clMember.getKind() == Tree.Kind.VARIABLE) {
0743: VariableTree var = (VariableTree) clMember;
0744: if (varName.equals(var
0745: .getName().toString())) {
0746: variable = var;
0747: }
0748: }
0749: }
0750: }
0751: if (variable != null) {
0752: Tree type = variable.getType();
0753: if (type.getKind() == Tree.Kind.PARAMETERIZED_TYPE) {
0754: ParameterizedTypeTree params = (ParameterizedTypeTree) type;
0755: List<? extends Tree> args = params
0756: .getTypeArguments();
0757: if (args.size() == 1) {
0758: Tree tree = args.get(0);
0759: result[0] = treeToType(cc,
0760: tree, comp
0761: .getFormModel());
0762: }
0763: }
0764: }
0765: }
0766:
0767: public void cancel() {
0768: }
0769: }, true);
0770: } catch (IOException ioex) {
0771: Logger.getLogger(BindingDesignSupport.class.getName()).log(
0772: Level.INFO, ioex.getMessage(), ioex);
0773: }
0774: if (result[0] == null) {
0775: // fallback - covers the situation where the component
0776: // has been added but the code hasn't been generated yet
0777: Class clazz = comp.getBeanClass();
0778: if (clazz.getTypeParameters().length == 1) {
0779: try {
0780: Object value = comp.getSyntheticProperty(
0781: "typeParameters").getValue(); // NOI18N
0782: if (value instanceof String) {
0783: String type = (String) value;
0784: if (type.startsWith("<")) { // NOI18N
0785: type = type.substring(1, type.length() - 1);
0786: Map<String, TypeHelper> newMap = new HashMap<String, TypeHelper>();
0787: try {
0788: Class elemType = ClassPathUtils
0789: .loadClass(
0790: type,
0791: FormEditor
0792: .getFormDataObject(
0793: comp
0794: .getFormModel())
0795: .getFormFile());
0796: newMap.put(clazz.getTypeParameters()[0]
0797: .getName(), new TypeHelper(
0798: elemType));
0799: } catch (ClassNotFoundException cnfex) {
0800: newMap.put(clazz.getTypeParameters()[0]
0801: .getName(),
0802: new TypeHelper(type));
0803: }
0804: result[0] = new TypeHelper(type, newMap);
0805: }
0806: }
0807: } catch (Exception ex) {
0808: Logger.getLogger(
0809: BindingDesignSupport.class.getName()).log(
0810: Level.INFO, ex.getMessage(), ex);
0811: }
0812: }
0813: }
0814: return result[0];
0815: }
0816:
0817: /**
0818: * Determines type of the binding described by the given component and source path.
0819: *
0820: * @param comp source of the binding.
0821: * @param sourcePath binding path from the source.
0822: * @return type of the binding.
0823: */
0824: public TypeHelper determineType(RADComponent comp, String sourcePath) {
0825: String[] path = parsePath(sourcePath);
0826: TypeHelper type = determineType(comp);
0827: for (int i = 0; i < path.length; i++) {
0828: String pathItem = path[i];
0829: List<BindingDescriptor> descriptors = getAllBindingDescriptors(type);
0830: BindingDescriptor descriptor = findDescriptor(descriptors,
0831: pathItem);
0832: if (descriptor == null)
0833: return new TypeHelper();
0834: type = descriptor.getGenericValueType();
0835: if (type == null) {
0836: if (javax.swing.JTable.class.isAssignableFrom(comp
0837: .getBeanClass())
0838: || javax.swing.JList.class
0839: .isAssignableFrom(comp.getBeanClass())
0840: || javax.swing.JComboBox.class
0841: .isAssignableFrom(comp.getBeanClass())) {
0842: MetaBinding binding = comp.getBindingProperty(
0843: "elements").getValue(); // NOI18N
0844: if (binding != null) {
0845: RADComponent subComp = binding.getSource();
0846: String subSourcePath = binding.getSourcePath();
0847: subSourcePath = (subSourcePath == null) ? null
0848: : BindingDesignSupport
0849: .unwrapSimpleExpression(subSourcePath);
0850: // PENDING beware of stack overflow
0851: TypeHelper t = determineType(subComp,
0852: subSourcePath);
0853: if ("selectedElement".equals(pathItem)
0854: || pathItem
0855: .startsWith("selectedElement_")) { // NOI18N
0856: type = typeOfElement(t);
0857: } else if (pathItem
0858: .startsWith("selectedElements")
0859: || "elements".equals(pathItem)) { // NOI18N
0860: type = t;
0861: }
0862: } else {
0863: type = new TypeHelper();
0864: }
0865: }
0866: }
0867: }
0868: return type;
0869: }
0870:
0871: /**
0872: * Finds descriptor that corresponds to the given binding path.
0873: *
0874: * @param descriptors list of descriptors that should be searched.
0875: * @param path binding path to find descriptor for.
0876: * @return descriptor that corresponds to the given binding path.
0877: */
0878: private static BindingDescriptor findDescriptor(
0879: List<BindingDescriptor> descriptors, String path) {
0880: for (BindingDescriptor descriptor : descriptors) {
0881: if (descriptor.getPath().equals(path))
0882: return descriptor;
0883: }
0884: return null;
0885: }
0886:
0887: /**
0888: * Parses binding path into segments.
0889: *
0890: * @param path path to parse.
0891: * @return segments of the binding path. The returned value cannot be <code>null</code>.
0892: */
0893: private static String[] parsePath(String path) {
0894: if (path == null)
0895: return new String[0];
0896: List<String> pathItems = new LinkedList<String>();
0897: int index;
0898: while ((index = path.indexOf('.')) != -1) {
0899: pathItems.add(path.substring(0, index));
0900: path = path.substring(index + 1);
0901: }
0902: pathItems.add(path);
0903: return pathItems.toArray(new String[pathItems.size()]);
0904: }
0905:
0906: /**
0907: * Returns type of element of the given type - expects type that implements
0908: * <code>Collection</code> interface.
0909: *
0910: * @param type type that implements <code>Collection</code> interface.
0911: * @return type of element of the given type.
0912: */
0913: static TypeHelper typeOfElement(TypeHelper type) {
0914: Type t = type.getType();
0915: TypeHelper elemType = new TypeHelper();
0916: if (t instanceof ParameterizedType) {
0917: ParameterizedType pt = (ParameterizedType) t;
0918: Type[] args = pt.getActualTypeArguments();
0919: // PENDING generalize and improve - track the type variables to the nearest
0920: // known collection superclass or check parameter type of add(E o) method
0921: if (args.length == 1) { // The only argument should be type of the collection element
0922: Type tt = args[0];
0923: elemType = new TypeHelper(tt, type.getActualTypeArgs());
0924: }
0925: } else if (t instanceof Class) {
0926: Class classa = (Class) t;
0927: TypeVariable[] tvar = classa.getTypeParameters();
0928: // PENDING dtto
0929: Map<String, TypeHelper> actualTypeArgs = type
0930: .getActualTypeArgs();
0931: if ((actualTypeArgs != null) && (tvar.length == 1)) {
0932: TypeHelper tt = actualTypeArgs.get(tvar[0].getName());
0933: if (tt != null) {
0934: if (tt.getType() == null) {
0935: elemType = tt;
0936: } else {
0937: Type typ = FormUtils.typeToClass(tt);
0938: elemType = new TypeHelper(typ, actualTypeArgs);
0939: }
0940: }
0941: }
0942: }
0943: return elemType;
0944: }
0945:
0946: public void establishUpdatedBindings(RADComponent metacomp,
0947: boolean recursive, Map map, BindingGroup group,
0948: boolean inModel) {
0949: for (MetaBinding bindingDef : collectBindingDefs(metacomp,
0950: recursive)) {
0951: RADComponent sourceComp = bindingDef.getSource();
0952: RADComponent targetComp = bindingDef.getTarget();
0953: if (sourceComp.isInModel() && targetComp.isInModel()) {
0954: if (inModel) {
0955: addBindingInModel(bindingDef);
0956: } else {
0957: Object source = null;
0958: if (map != null)
0959: source = map.get(sourceComp.getId());
0960: if (source == null)
0961: source = sourceComp.getBeanInstance(); // also used if clone not available
0962: Object target = map != null ? map.get(targetComp
0963: .getId()) : targetComp.getBeanInstance();
0964: if (source != null && target != null)
0965: addBinding(bindingDef, source, target, group,
0966: false);
0967: }
0968: }
0969: }
0970: }
0971:
0972: public static void establishOneOffBindings(RADComponent metacomp,
0973: boolean recursive, Map map, BindingGroup group) {
0974: for (MetaBinding bindingDef : collectBindingDefs(metacomp,
0975: recursive)) {
0976: RADComponent sourceComp = bindingDef.getSource();
0977: RADComponent targetComp = bindingDef.getTarget();
0978: Object source = null;
0979: if (map != null)
0980: source = map.get(sourceComp.getId());
0981: if (source == null)
0982: source = sourceComp.getBeanInstance(); // also used if clone not available
0983: Object target = map != null ? map.get(targetComp.getId())
0984: : targetComp.getBeanInstance();
0985: if (source != null && target != null)
0986: createBinding(bindingDef, source, target, group, null);
0987: }
0988: }
0989:
0990: private void releaseBindings(RADComponent metacomp,
0991: boolean recursive) {
0992: for (MetaBinding bindingDef : collectBindingDefs(metacomp,
0993: recursive)) {
0994: removeBindings(bindingDef); // unbinds and removes all bindings
0995: // created according to this definition
0996: }
0997: }
0998:
0999: private static Collection<MetaBinding> collectBindingDefs(
1000: RADComponent metacomp, boolean recursive) {
1001: Collection<MetaBinding> col = collectBindingDefs(metacomp,
1002: recursive, null);
1003: if (col == null)
1004: col = Collections.emptyList();
1005: return col;
1006: }
1007:
1008: private static Collection<MetaBinding> collectBindingDefs(
1009: RADComponent metacomp, boolean recursive,
1010: Collection<MetaBinding> col) {
1011: for (BindingProperty bProp : metacomp
1012: .getKnownBindingProperties()) {
1013: MetaBinding bindingDef = bProp.getValue();
1014: if (bindingDef != null) {
1015: if (col == null)
1016: col = new LinkedList<MetaBinding>();
1017: col.add(bindingDef);
1018: }
1019: }
1020:
1021: if (recursive && metacomp instanceof ComponentContainer) {
1022: for (RADComponent subcomp : ((ComponentContainer) metacomp)
1023: .getSubBeans()) {
1024: col = collectBindingDefs(subcomp, recursive, col);
1025: }
1026: }
1027:
1028: return col;
1029: }
1030:
1031: private void addBindingInModel(MetaBinding bindingDef) {
1032: addBinding(bindingDef,
1033: bindingDef.getSource().getBeanInstance(), bindingDef
1034: .getTarget().getBeanInstance(), bindingGroup,
1035: true);
1036: }
1037:
1038: /**
1039: * Creates binding according to given MetaBinding between given source and
1040: * target objects. The binding is registered, so it is automatically unbound
1041: * and removed when the MetaBinding is removed (or the source/target component).
1042: *
1043: * @param bindingDef description of the binding
1044: * @param source binding source
1045: * @param target binding target
1046: * @param group binding group where the binding should be added
1047: * @param inModel determines whether we are creating binding in the model
1048: */
1049: public void addBinding(MetaBinding bindingDef, Object source,
1050: Object target, BindingGroup group, boolean inModel) {
1051: if (inModel) {
1052: if (modelBindings.get(bindingDef) == null) {
1053: modelBindings.put(bindingDef, createBinding(bindingDef,
1054: source, target, group, bindingToGroup));
1055: }
1056: } else {
1057: List<Binding> establishedBindings = bindingsMap
1058: .get(bindingDef);
1059: if (establishedBindings != null) {
1060: for (Binding binding : establishedBindings) {
1061: if (binding.getSourceObject() == source
1062: && binding.getTargetObject() == target)
1063: return; // this binding already exists
1064: }
1065: } else {
1066: establishedBindings = new LinkedList<Binding>();
1067: bindingsMap.put(bindingDef, establishedBindings);
1068: }
1069: establishedBindings.add(createBinding(bindingDef, source,
1070: target, group, bindingToGroup));
1071: }
1072: }
1073:
1074: private static String actualTargetPath(MetaBinding bindingDef) {
1075: String targetPath = bindingDef.getTargetPath();
1076: if ("text".equals(targetPath)) { // NOI18N
1077: Class<?> targetClass = bindingDef.getTarget()
1078: .getBeanClass();
1079: if (JTextComponent.class.isAssignableFrom(targetClass)) {
1080: String strategy = bindingDef
1081: .getParameter(MetaBinding.TEXT_CHANGE_STRATEGY);
1082: if (MetaBinding.TEXT_CHANGE_ON_ACTION_OR_FOCUS_LOST
1083: .equals(strategy)) {
1084: targetPath += "_ON_ACTION_OR_FOCUS_LOST"; // NOI18N
1085: } else if (MetaBinding.TEXT_CHANGE_ON_FOCUS_LOST
1086: .equals(strategy)) {
1087: targetPath += "_ON_FOCUS_LOST"; // NOI18N
1088: }
1089: }
1090: } else if ("selectedElement".equals(targetPath)
1091: || "selectedElements".equals(targetPath)) { // NOI18N
1092: Class<?> targetClass = bindingDef.getTarget()
1093: .getBeanClass();
1094: if (JList.class.isAssignableFrom(targetClass)
1095: || JTable.class.isAssignableFrom(targetClass)
1096: || JComboBox.class.isAssignableFrom(targetClass)) {
1097: String value = bindingDef
1098: .getParameter(MetaBinding.IGNORE_ADJUSTING_PARAMETER);
1099: if ("Y".equals(value)) { // NOI18N
1100: targetPath += "_IGNORE_ADJUSTING"; // NOI18N
1101: }
1102: }
1103: } else if ("value".equals(targetPath)) { // NOI18N
1104: Class<?> targetClass = bindingDef.getTarget()
1105: .getBeanClass();
1106: if (JSlider.class.isAssignableFrom(targetClass)) {
1107: String value = bindingDef
1108: .getParameter(MetaBinding.IGNORE_ADJUSTING_PARAMETER);
1109: if ("Y".equals(value)) { // NOI18N
1110: targetPath += "_IGNORE_ADJUSTING"; // NOI18N
1111: }
1112: }
1113: }
1114: return targetPath;
1115: }
1116:
1117: private static void generateTargetProperty(MetaBinding bindingDef,
1118: StringBuilder buf) {
1119: String targetPath = actualTargetPath(bindingDef);
1120: String property = BeanProperty.class.getName() + ".create(\""
1121: + targetPath + "\")"; // NOI18N
1122: buf.append(property);
1123: }
1124:
1125: private static Property createTargetProperty(MetaBinding bindingDef) {
1126: String targetPath = actualTargetPath(bindingDef);
1127: Property property = BeanProperty.create(targetPath);
1128: return property;
1129: }
1130:
1131: public static String generateBinding(BindingProperty prop,
1132: StringBuilder buf) {
1133: String variable;
1134: MetaBinding bindingDef = prop.getValue();
1135: // Update strategy
1136: int updateStrategy = bindingDef.getUpdateStrategy();
1137: String strategy = AutoBinding.class.getName()
1138: + ".UpdateStrategy."; // NOI18N
1139: if (updateStrategy == MetaBinding.UPDATE_STRATEGY_READ) {
1140: strategy += "READ"; // NOI18N
1141: } else if (updateStrategy == MetaBinding.UPDATE_STRATEGY_READ_ONCE) {
1142: strategy += "READ_ONCE"; // NOI18N
1143: } else {
1144: strategy += "READ_WRITE"; // NOI18N
1145: }
1146: strategy += ", "; // NOI18N
1147:
1148: RADComponent target = bindingDef.getTarget();
1149: FormModel formModel = target.getFormModel();
1150: JavaCodeGenerator generator = (JavaCodeGenerator) FormEditor
1151: .getCodeGenerator(formModel);
1152: Class targetClass = target.getBeanClass();
1153: String targetPath = bindingDef.getTargetPath();
1154: String sourcePath = bindingDef.getSourcePath();
1155: Class<?> sourceClass = bindingDef.getSource().getBeanClass();
1156: if ("elements".equals(targetPath)
1157: && JTable.class.isAssignableFrom(targetClass)
1158: && (List.class.isAssignableFrom(sourceClass) || (sourcePath != null))) { // NOI18N
1159: String elVariable = elVariableHelper(sourcePath, buf,
1160: generator);
1161: variable = generator.getBindingDescriptionVariable(
1162: JTableBinding.class, buf, false);
1163: if (variable == null) {
1164: variable = generator.getBindingDescriptionVariable(
1165: JTableBinding.class, buf, true);
1166: buf.append(' ');
1167: }
1168: buf.append(variable);
1169: buf.append(" = "); // NOI18N
1170: buf.append(SwingBindings.class.getName()).append(
1171: ".createJTableBinding("); // NOI18N
1172: buf.append(strategy);
1173: buf
1174: .append(JavaCodeGenerator.getExpressionJavaString(
1175: bindingDef.getSource().getCodeExpression(),
1176: "this")); // NOI18N
1177: buf.append(", "); // NOI18N
1178: if (sourcePath != null) {
1179: buf.append(elVariable);
1180: buf.append(", "); // NOI18N
1181: }
1182: buf
1183: .append(JavaCodeGenerator.getExpressionJavaString(
1184: bindingDef.getTarget().getCodeExpression(),
1185: "this")); // NOI18N
1186: buildBindingNameCode(prop, buf);
1187: buf.append(");\n"); // NOI18N
1188: if (bindingDef.hasSubBindings()) {
1189: for (MetaBinding sub : bindingDef.getSubBindings()) {
1190: String columnVariable = generator
1191: .getBindingDescriptionVariable(
1192: JTableBinding.ColumnBinding.class,
1193: buf, false);
1194: if (columnVariable == null) {
1195: columnVariable = generator
1196: .getBindingDescriptionVariable(
1197: JTableBinding.ColumnBinding.class,
1198: buf, true);
1199: buf.append(' ');
1200: }
1201: buf.append(columnVariable);
1202: buf.append(" = "); // NOI18N
1203: buf.append(variable);
1204: buf.append(".addColumnBinding("); // NOI18N
1205: buf.append(ELProperty.class.getName());
1206: buf.append(".create(\""); // NOI18N
1207: buf.append(sub.getSourcePath());
1208: buf.append("\"));\n"); // NOI18N
1209: String title = sub
1210: .getParameter(MetaBinding.NAME_PARAMETER);
1211: if (title == null) {
1212: title = sub.getSourcePath();
1213: if (isSimpleExpression(title)) {
1214: title = unwrapSimpleExpression(title);
1215: title = capitalize(title);
1216: }
1217: }
1218: if ((title != null) && (!"null".equals(title))) { // NOI18N
1219: buf.append(columnVariable);
1220: buf.append(".setColumnName(\""); // NOI18N
1221: buf.append(title);
1222: buf.append("\");\n"); // NOI18N
1223: }
1224: String columnClass = sub
1225: .getParameter(MetaBinding.TABLE_COLUMN_CLASS_PARAMETER);
1226: if (columnClass != null) {
1227: buf.append(columnVariable);
1228: buf.append(".setColumnClass("); // NOI18N
1229: buf.append(columnClass);
1230: buf.append(");\n"); // NOI18N
1231: }
1232: String editable = sub
1233: .getParameter(MetaBinding.EDITABLE_PARAMETER);
1234: if (editable != null) {
1235: buf.append(columnVariable);
1236: buf.append(".setEditable("); // NOI18N
1237: buf.append(editable);
1238: buf.append(");\n"); // NOI18N
1239: }
1240: }
1241: }
1242: } else if ("elements".equals(targetPath)
1243: && javax.swing.JList.class
1244: .isAssignableFrom(targetClass)
1245: && (List.class.isAssignableFrom(sourceClass) || (sourcePath != null))) { // NOI18N
1246: String elVariable = elVariableHelper(sourcePath, buf,
1247: generator);
1248: variable = generator.getBindingDescriptionVariable(
1249: JListBinding.class, buf, false);
1250: if (variable == null) {
1251: variable = generator.getBindingDescriptionVariable(
1252: JListBinding.class, buf, true);
1253: buf.append(' ');
1254: }
1255: buf.append(variable);
1256: buf.append(" = "); // NOI18N
1257: buf.append(SwingBindings.class.getName()).append(
1258: ".createJListBinding("); // NOI18N
1259: buf.append(strategy);
1260: buf
1261: .append(JavaCodeGenerator.getExpressionJavaString(
1262: bindingDef.getSource().getCodeExpression(),
1263: "this")); // NOI18N
1264: buf.append(", "); // NOI18N
1265: if (sourcePath != null) {
1266: buf.append(elVariable);
1267: buf.append(", "); // NOI18N
1268: }
1269: buf
1270: .append(JavaCodeGenerator.getExpressionJavaString(
1271: bindingDef.getTarget().getCodeExpression(),
1272: "this")); // NOI18N
1273: buildBindingNameCode(prop, buf);
1274: buf.append(");\n"); // NOI18N
1275: String detailPath = bindingDef
1276: .getParameter(MetaBinding.DISPLAY_PARAMETER);
1277: if (detailPath != null) {
1278: buf.append(variable);
1279: buf.append(".setDetailBinding("); // NOI18N
1280: buf.append(ELProperty.class.getName());
1281: buf.append(".create(\""); // NOI18N
1282: buf.append(detailPath);
1283: buf.append("\"));\n"); // NOI18N
1284: }
1285: } else if ("elements".equals(targetPath)
1286: && javax.swing.JComboBox.class
1287: .isAssignableFrom(targetClass)
1288: && (List.class.isAssignableFrom(sourceClass) || (sourcePath != null))) { // NOI18N
1289: String elVariable = elVariableHelper(sourcePath, buf,
1290: generator);
1291: variable = generator.getBindingDescriptionVariable(
1292: JComboBoxBinding.class, buf, false);
1293: if (variable == null) {
1294: variable = generator.getBindingDescriptionVariable(
1295: JComboBoxBinding.class, buf, true);
1296: buf.append(' ');
1297: }
1298: buf.append(variable);
1299: buf.append(" = "); // NOI18N
1300: buf.append(SwingBindings.class.getName()).append(
1301: ".createJComboBoxBinding("); // NOI18N
1302: buf.append(strategy);
1303: buf
1304: .append(JavaCodeGenerator.getExpressionJavaString(
1305: bindingDef.getSource().getCodeExpression(),
1306: "this")); // NOI18N
1307: buf.append(", "); // NOI18N
1308: if (sourcePath != null) {
1309: buf.append(elVariable);
1310: buf.append(", "); // NOI18N
1311: }
1312: buf
1313: .append(JavaCodeGenerator.getExpressionJavaString(
1314: bindingDef.getTarget().getCodeExpression(),
1315: "this")); // NOI18N
1316: buildBindingNameCode(prop, buf);
1317: buf.append(");\n"); // NOI18N
1318: } else {
1319: variable = generator.getBindingDescriptionVariable(
1320: Binding.class, buf, false);
1321: StringBuilder sb = new StringBuilder();
1322: if (variable == null) {
1323: variable = generator.getBindingDescriptionVariable(
1324: Binding.class, buf, true);
1325: buf.append(' ');
1326: buf.append(sb);
1327: }
1328: buf.append(variable);
1329: buf.append(" = "); // NOI18N
1330: buf.append(Bindings.class.getName()).append(sb).append(
1331: ".createAutoBinding("); // NOI18N
1332: buf.append(strategy);
1333: buildBindingParamsCode(prop, buf);
1334: }
1335: return variable;
1336: }
1337:
1338: private static ELProperty createELProperty(String path) {
1339: ELProperty property;
1340: try {
1341: property = ELProperty.create(path);
1342: } catch (Exception ex) {
1343: Logger.getLogger(BindingDesignSupport.class.getName()).log(
1344: Level.INFO, ex.getMessage(), ex);
1345: // fallback
1346: property = ELProperty.create("error"); // NOI18N
1347: }
1348: return property;
1349: }
1350:
1351: private static String elVariableHelper(String sourcePath,
1352: StringBuilder buf, JavaCodeGenerator generator) {
1353: String elVariable = null;
1354: if (sourcePath != null) {
1355: elVariable = generator.getBindingDescriptionVariable(
1356: ELProperty.class, buf, false);
1357: if (elVariable == null) {
1358: elVariable = generator.getBindingDescriptionVariable(
1359: ELProperty.class, buf, true);
1360: buf.append(' ');
1361: }
1362: buf.append(elVariable);
1363: buf.append(" = "); // NOI18N
1364: buf.append(ELProperty.class.getName());
1365: buf.append(".create(\""); // NOI18N
1366: buf.append(sourcePath);
1367: buf.append("\");\n"); // NOI18N
1368: }
1369: return elVariable;
1370: }
1371:
1372: private static void buildBindingParamsCode(BindingProperty prop,
1373: StringBuilder buf) {
1374: MetaBinding bindingDef = prop.getValue();
1375: String sourcePath = bindingDef.getSourcePath();
1376: String targetPath = bindingDef.getTargetPath();
1377: buf.append(JavaCodeGenerator.getExpressionJavaString(bindingDef
1378: .getSource().getCodeExpression(), "this")); // NOI18N
1379: buf.append(", "); // NOI18N
1380: if (sourcePath != null) {
1381: buf.append(ELProperty.class.getName());
1382: buf.append(".create(\""); // NOI18N
1383: buf.append(sourcePath);
1384: buf.append("\")"); // NOI18N
1385: } else {
1386: buf.append(ObjectProperty.class.getName());
1387: buf.append(".create()"); // NOI18N
1388: }
1389: buf.append(", "); // NOI18N
1390: buf.append(JavaCodeGenerator.getExpressionJavaString(bindingDef
1391: .getTarget().getCodeExpression(), "this")); // NOI18N
1392: buf.append(", "); // NOI18N
1393: if (targetPath != null) {
1394: generateTargetProperty(bindingDef, buf);
1395: } else {
1396: buf.append(ObjectProperty.class.getName());
1397: buf.append(".create()"); // NOI18N
1398: }
1399: buildBindingNameCode(prop, buf);
1400: buf.append(");\n"); // NOI18N
1401: }
1402:
1403: private static void buildBindingNameCode(BindingProperty prop,
1404: StringBuilder buf) {
1405: MetaBinding bindingDef = prop.getValue();
1406: if (bindingDef.isNameSpecified()) {
1407: try {
1408: FormProperty property = prop.getNameProperty();
1409: Object value = property.getValue();
1410: if (value != null) {
1411: buf.append(", "); // NOI18N
1412: buf.append(property.getJavaInitializationString());
1413: }
1414: } catch (IllegalAccessException iaex) {
1415: iaex.printStackTrace();
1416: } catch (InvocationTargetException itex) {
1417: itex.printStackTrace();
1418: }
1419: }
1420: }
1421:
1422: private static Binding createBinding0(MetaBinding bindingDef,
1423: Object source, Object target, BindingGroup group) {
1424: String name = null;
1425: if (bindingDef.isNameSpecified()) {
1426: BindingProperty prop = bindingDef.getTarget()
1427: .getBindingProperty(bindingDef.getTargetPath());
1428: FormProperty nameProp = prop.getNameProperty();
1429: try {
1430: Object value = nameProp.getRealValue();
1431: if ((value != null) && (value instanceof String)) {
1432: name = (String) value;
1433: }
1434: } catch (IllegalAccessException iaex) {
1435: Logger.getLogger(BindingDesignSupport.class.getName())
1436: .log(Level.INFO, iaex.getMessage(), iaex);
1437: } catch (InvocationTargetException itex) {
1438: Logger.getLogger(BindingDesignSupport.class.getName())
1439: .log(Level.INFO, itex.getMessage(), itex);
1440: }
1441: if ((name != null) && group.getBinding(name) != null) {
1442: Logger.getLogger(BindingDesignSupport.class.getName())
1443: .log(
1444: Level.INFO,
1445: "More than one binding with name: "
1446: + name); // NOI18N
1447: name = null; // ignore name parameter
1448: }
1449: }
1450: AutoBinding.UpdateStrategy updateStrategy = AutoBinding.UpdateStrategy.READ_WRITE;
1451: switch (bindingDef.getUpdateStrategy()) {
1452: case MetaBinding.UPDATE_STRATEGY_READ_WRITE:
1453: updateStrategy = AutoBinding.UpdateStrategy.READ_WRITE;
1454: break;
1455: case MetaBinding.UPDATE_STRATEGY_READ:
1456: updateStrategy = AutoBinding.UpdateStrategy.READ;
1457: break;
1458: case MetaBinding.UPDATE_STRATEGY_READ_ONCE:
1459: updateStrategy = AutoBinding.UpdateStrategy.READ_ONCE;
1460: break;
1461: default:
1462: assert false;
1463: }
1464: Binding<Object, ?, Object, ?> binding;
1465: Property targetProperty = createTargetProperty(bindingDef);
1466: Property sourceProperty = (bindingDef.getSourcePath() == null) ? ObjectProperty
1467: .create()
1468: : createELProperty(bindingDef.getSourcePath());
1469: RADComponent targetComp = bindingDef.getTarget();
1470: String targetPath = bindingDef.getTargetPath();
1471: String sourcePath = bindingDef.getSourcePath();
1472: if ("elements".equals(targetPath)
1473: && javax.swing.JTable.class.isAssignableFrom(targetComp
1474: .getBeanClass())
1475: && ((source instanceof List) || (sourcePath != null))) { // NOI18N
1476: JTableBinding<Object, Object, Object> tableBinding;
1477: if (sourcePath == null) {
1478: tableBinding = SwingBindings.createJTableBinding(
1479: updateStrategy, (List) source, (JTable) target,
1480: name);
1481: } else {
1482: tableBinding = SwingBindings.createJTableBinding(
1483: updateStrategy, source, sourceProperty,
1484: (JTable) target, name);
1485: }
1486: if (bindingDef.hasSubBindings()) {
1487: Collection<MetaBinding> subBindings = bindingDef
1488: .getSubBindings();
1489: for (MetaBinding sub : subBindings) {
1490: JTableBinding.ColumnBinding columnBinding = tableBinding
1491: .addColumnBinding(createELProperty(sub
1492: .getSourcePath()));
1493: String title = sub
1494: .getParameter(MetaBinding.NAME_PARAMETER);
1495: if (title == null) {
1496: title = sub.getSourcePath();
1497: if (isSimpleExpression(title)) {
1498: title = unwrapSimpleExpression(title);
1499: title = capitalize(title);
1500: }
1501: }
1502: columnBinding.setColumnName(title);
1503: String columnClass = sub
1504: .getParameter(MetaBinding.TABLE_COLUMN_CLASS_PARAMETER);
1505: if (columnClass != null) {
1506: try {
1507: if ((columnClass != null)
1508: && columnClass.trim().endsWith(
1509: ".class")) { // NOI18N
1510: columnClass = columnClass.trim();
1511: columnClass = columnClass.substring(0,
1512: columnClass.length() - 6);
1513: }
1514: if (columnClass.indexOf('.') == -1) {
1515: String prefix = ""; // NOI18N
1516: while (columnClass.endsWith("[]")) { // NOI18N
1517: columnClass = columnClass
1518: .substring(0, columnClass
1519: .length() - 2);
1520: prefix += "["; // NOI18N
1521: }
1522: if ("".equals(prefix)) { // NOI18N
1523: columnClass = "java.lang."
1524: + columnClass; // NOI18N
1525: } else {
1526: String suffix = columnClass;
1527: if (columnClass.equals("boolean")) { // NOI18N
1528: suffix = "Z"; // NOI18N
1529: } else if (columnClass
1530: .equals("byte")) { // NOI18N
1531: suffix = "B"; // NOI18N
1532: } else if (columnClass
1533: .equals("char")) { // NOI18N
1534: suffix = "C"; // NOI18N
1535: } else if (columnClass
1536: .equals("char")) { // NOI18N
1537: suffix = "D"; // NOI18N
1538: } else if (columnClass
1539: .equals("float")) { // NOI18N
1540: suffix = "F"; // NOI18N
1541: } else if (columnClass
1542: .equals("int")) { // NOI18N
1543: suffix = "I"; // NOI18N
1544: } else if (columnClass
1545: .equals("long")) { // NOI18N
1546: suffix = "J"; // NOI18N
1547: } else if (columnClass
1548: .equals("short")) { // NOI18N
1549: suffix = "S"; // NOI18N
1550: } else {
1551: prefix += "L"; // NOI18N
1552: if (suffix.indexOf('.') == -1) {
1553: suffix = "java.lang."
1554: + suffix; // NOI18N
1555: }
1556: suffix += ";"; // NOI18N
1557: }
1558: columnClass = prefix + suffix;
1559: }
1560: }
1561: Class<?> clazz = FormUtils.loadClass(
1562: columnClass, bindingDef.getSource()
1563: .getFormModel());
1564: columnBinding.setColumnClass(clazz);
1565: } catch (ClassNotFoundException cnfex) {
1566: Logger.getLogger(
1567: BindingDesignSupport.class
1568: .getName()).log(Level.INFO,
1569: cnfex.getMessage(), cnfex);
1570: }
1571: }
1572: String editable = sub
1573: .getParameter(MetaBinding.EDITABLE_PARAMETER);
1574: if (editable != null) {
1575: Boolean value = "false".equals(editable) ? Boolean.FALSE
1576: : Boolean.TRUE; // NOI18N
1577: columnBinding.setEditable(value);
1578: }
1579: }
1580: }
1581: binding = tableBinding;
1582: } else if ("elements".equals(targetPath)
1583: && javax.swing.JList.class.isAssignableFrom(targetComp
1584: .getBeanClass())
1585: && ((source instanceof List) || (sourcePath != null))) { // NOI18N
1586: JListBinding listBinding;
1587: if (sourcePath == null) {
1588: listBinding = SwingBindings.createJListBinding(
1589: updateStrategy, (List) source, (JList) target,
1590: name);
1591: } else {
1592: listBinding = SwingBindings.createJListBinding(
1593: updateStrategy, source, sourceProperty,
1594: (JList) target, name);
1595: }
1596: String detailPath = bindingDef
1597: .getParameter(MetaBinding.DISPLAY_PARAMETER);
1598: if (detailPath != null) {
1599: listBinding
1600: .setDetailBinding(createELProperty(detailPath));
1601: }
1602: binding = listBinding;
1603: } else if ("elements".equals(targetPath)
1604: && javax.swing.JComboBox.class
1605: .isAssignableFrom(targetComp.getBeanClass())
1606: && ((source instanceof List) || (sourcePath != null))) { // NOI18N
1607: JComboBoxBinding comboBinding;
1608: if (sourcePath == null) {
1609: comboBinding = SwingBindings.createJComboBoxBinding(
1610: updateStrategy, (List) source,
1611: (JComboBox) target, name);
1612: } else {
1613: comboBinding = SwingBindings.createJComboBoxBinding(
1614: updateStrategy, source, sourceProperty,
1615: (JComboBox) target, name);
1616: }
1617: // String detailPath = bindingDef.getParameter(MetaBinding.DISPLAY_PARAMETER);
1618: // if (detailPath != null) {
1619: // comboBinding.setDetailBinding(createELProperty(detailPath));
1620: // }
1621: binding = comboBinding;
1622: } else {
1623: binding = Bindings.createAutoBinding(updateStrategy,
1624: source, sourceProperty, target, targetProperty,
1625: name);
1626: }
1627: return binding;
1628: }
1629:
1630: private static Binding createBinding(MetaBinding bindingDef,
1631: Object source, Object target, BindingGroup group,
1632: Map<Binding, BindingGroup> bindingToGroup) {
1633: Binding<Object, Object, Object, Object> binding = (Binding<Object, Object, Object, Object>) createBinding0(
1634: bindingDef, source, target, group);
1635: if (bindingDef.isNullValueSpecified()) {
1636: BindingProperty prop = bindingDef.getTarget()
1637: .getBindingProperty(bindingDef.getTargetPath());
1638: FormProperty nullProp = prop.getNullValueProperty();
1639: try {
1640: Object value = nullProp.getRealValue();
1641: if (value != null) {
1642: binding.setSourceNullValue(value);
1643: }
1644: } catch (IllegalAccessException iaex) {
1645: Logger.getLogger(BindingDesignSupport.class.getName())
1646: .log(Level.INFO, iaex.getMessage(), iaex);
1647: } catch (InvocationTargetException itex) {
1648: Logger.getLogger(BindingDesignSupport.class.getName())
1649: .log(Level.INFO, itex.getMessage(), itex);
1650: }
1651: }
1652: if (bindingDef.isIncompletePathValueSpecified()) {
1653: BindingProperty prop = bindingDef.getTarget()
1654: .getBindingProperty(bindingDef.getTargetPath());
1655: FormProperty incompleteProp = prop
1656: .getIncompleteValueProperty();
1657: try {
1658: Object value = incompleteProp.getRealValue();
1659: if (value != null) {
1660: binding.setSourceUnreadableValue(value);
1661: }
1662: } catch (IllegalAccessException iaex) {
1663: Logger.getLogger(BindingDesignSupport.class.getName())
1664: .log(Level.INFO, iaex.getMessage(), iaex);
1665: } catch (InvocationTargetException itex) {
1666: Logger.getLogger(BindingDesignSupport.class.getName())
1667: .log(Level.INFO, itex.getMessage(), itex);
1668: }
1669: }
1670: if (bindingDef.isConverterSpecified()) {
1671: BindingProperty prop = bindingDef.getTarget()
1672: .getBindingProperty(bindingDef.getTargetPath());
1673: FormProperty converterProp = prop.getConverterProperty();
1674: try {
1675: Object value = converterProp.getRealValue();
1676: if ((value != null) && (value instanceof Converter)) {
1677: binding.setConverter((Converter) value);
1678: }
1679: } catch (IllegalAccessException iaex) {
1680: Logger.getLogger(BindingDesignSupport.class.getName())
1681: .log(Level.INFO, iaex.getMessage(), iaex);
1682: } catch (InvocationTargetException itex) {
1683: Logger.getLogger(BindingDesignSupport.class.getName())
1684: .log(Level.INFO, itex.getMessage(), itex);
1685: }
1686: }
1687: if (bindingDef.isValidatorSpecified()) {
1688: BindingProperty prop = bindingDef.getTarget()
1689: .getBindingProperty(bindingDef.getTargetPath());
1690: FormProperty validatorProp = prop.getValidatorProperty();
1691: try {
1692: Object value = validatorProp.getRealValue();
1693: if ((value != null) && (value instanceof Validator)) {
1694: binding.setValidator((Validator) value);
1695: }
1696: } catch (IllegalAccessException iaex) {
1697: Logger.getLogger(BindingDesignSupport.class.getName())
1698: .log(Level.INFO, iaex.getMessage(), iaex);
1699: } catch (InvocationTargetException itex) {
1700: Logger.getLogger(BindingDesignSupport.class.getName())
1701: .log(Level.INFO, itex.getMessage(), itex);
1702: }
1703: }
1704: group.addBinding(binding);
1705: if (bindingToGroup != null) {
1706: bindingToGroup.put(binding, group);
1707: }
1708:
1709: try {
1710: binding.bind();
1711: } catch (Exception ex) {
1712: Logger.getLogger(BindingDesignSupport.class.getName()).log(
1713: Level.INFO, ex.getMessage(), ex);
1714: }
1715: return binding;
1716: }
1717:
1718: private void removeBindings(MetaBinding bindingDef) {
1719: removeBindingInModel(bindingDef);
1720: List<Binding> establishedBindings = bindingsMap.get(bindingDef);
1721: if (establishedBindings != null) {
1722: for (Binding binding : establishedBindings) {
1723: removeBinding(binding);
1724: }
1725: bindingsMap.remove(bindingDef);
1726: }
1727: }
1728:
1729: private void removeBinding(Binding binding) {
1730: BindingGroup group = bindingToGroup.remove(binding);
1731: // It may happen that binding.bind() fails. Binding may
1732: // stay unbound in such situation and binding.unbind()
1733: // throws exception is this case
1734: if (binding.isBound()) {
1735: binding.unbind();
1736: }
1737: group.removeBinding(binding);
1738: }
1739:
1740: private void removeBindingInModel(MetaBinding bindingDef) {
1741: Binding binding = modelBindings.remove(bindingDef);
1742: if (binding != null) {
1743: removeBinding(binding);
1744: }
1745: }
1746:
1747: public static String capitalize(String title) {
1748: StringBuilder builder = new StringBuilder(title);
1749: boolean lastWasUpper = false;
1750: for (int i = 0; i < builder.length(); i++) {
1751: char aChar = builder.charAt(i);
1752: if (i == 0) {
1753: builder.setCharAt(i, Character.toUpperCase(aChar));
1754: lastWasUpper = true;
1755: } else if (Character.isUpperCase(aChar)) {
1756: if (!lastWasUpper) {
1757: builder.insert(i, ' ');
1758: }
1759: lastWasUpper = true;
1760: i++;
1761: } else {
1762: lastWasUpper = false;
1763: }
1764: }
1765: return builder.toString();
1766: }
1767:
1768: /**
1769: * Form model listener that updates the bindings.
1770: */
1771: private class ModelListener implements FormModelListener {
1772: public void formChanged(FormModelEvent[] events) {
1773: if (events == null)
1774: return;
1775:
1776: for (int i = 0; i < events.length; i++) {
1777: FormModelEvent ev = events[i];
1778: switch (ev.getChangeType()) {
1779: case FormModelEvent.BINDING_PROPERTY_CHANGED:
1780: if (ev.getSubPropertyName() == null) {
1781: changeBinding(ev.getOldBinding(), ev
1782: .getNewBinding());
1783: }
1784: break;
1785: case FormModelEvent.COMPONENT_REMOVED:
1786: releaseBindings(ev.getComponent(), true);
1787: break;
1788: case FormModelEvent.COMPONENT_ADDED:
1789: if (!ev.getCreatedDeleted()) {
1790: establishUpdatedBindings(ev.getComponent(),
1791: true, null, bindingGroup, true);
1792: }
1793: break;
1794: }
1795: }
1796: }
1797: }
1798:
1799: static class ModifiableBoolean {
1800: boolean value;
1801: }
1802:
1803: }
|