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.binding;
042:
043: import java.beans.PropertyDescriptor;
044: import java.util.ArrayList;
045: import java.util.Enumeration;
046: import java.util.StringTokenizer;
047: import java.util.TreeMap;
048: import javax.faces.component.UIData;
049: import java.awt.Component;
050: import java.awt.GridBagConstraints;
051: import java.awt.GridBagLayout;
052: import java.awt.Insets;
053: import java.awt.event.ActionEvent;
054: import java.awt.event.ActionListener;
055: import javax.swing.Icon;
056: import javax.swing.ImageIcon;
057: import javax.swing.JComponent;
058: import javax.swing.JLabel;
059: import javax.swing.JPanel;
060: import javax.swing.JScrollPane;
061: import javax.swing.JTree;
062: import javax.swing.SwingUtilities;
063: import javax.swing.event.TreeModelEvent;
064: import javax.swing.event.TreeModelListener;
065: import javax.swing.event.TreeSelectionEvent;
066: import javax.swing.event.TreeSelectionListener;
067: import javax.swing.tree.DefaultTreeCellRenderer;
068: import javax.swing.tree.DefaultTreeModel;
069: import javax.swing.tree.DefaultTreeSelectionModel;
070: import javax.swing.tree.TreeNode;
071: import javax.swing.tree.TreePath;
072: import javax.swing.tree.TreeSelectionModel;
073: import com.sun.rave.designtime.Constants;
074: import com.sun.rave.designtime.DesignBean;
075: import com.sun.rave.designtime.DesignContext;
076: import com.sun.rave.designtime.DesignProject;
077: import com.sun.rave.designtime.DesignProperty;
078: import com.sun.rave.designtime.faces.FacesDesignContext;
079: import com.sun.rave.designtime.faces.FacesDesignProject;
080: import org.netbeans.modules.visualweb.propertyeditors.binding.BindingTargetNode.Null;
081: import org.netbeans.modules.visualweb.propertyeditors.binding.nodes.ContextTargetNode;
082: import org.netbeans.modules.visualweb.propertyeditors.binding.nodes.DataProviderTargetNodeFactory;
083: import org.netbeans.modules.visualweb.propertyeditors.binding.nodes.MapTargetNodeFactory;
084: import org.netbeans.modules.visualweb.propertyeditors.binding.nodes.PropertyTargetNode;
085: import org.netbeans.modules.visualweb.propertyeditors.binding.nodes.ResultSetTargetNodeFactory;
086: import org.netbeans.modules.visualweb.propertyeditors.binding.nodes.UIDataVarNode;
087: import org.netbeans.modules.visualweb.propertyeditors.util.Bundle;
088: import com.sun.data.provider.DataProvider;
089: import javax.sql.RowSet;
090:
091: public class BindingTargetPanel extends JPanel {
092: JLabel targetLabel = new JLabel();
093: JScrollPane targetScroll = new JScrollPane();
094: JTree tree = new HiddenSelectionJTree();
095: BindingTargetNode.Root rootNode = new BindingTargetNode.Root();
096: DefaultTreeModel treeModel = new DefaultTreeModel(rootNode);
097: DefaultTreeSelectionModel treeSelectionModel = new DefaultTreeSelectionModel();
098: GridBagLayout gridBagLayout1 = new GridBagLayout();
099: GridBagConstraints customPanelConstraints = new GridBagConstraints(
100: 0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.WEST,
101: GridBagConstraints.HORIZONTAL, new Insets(-4, 12, 11, 0),
102: 0, 0);
103:
104: private DesignBean targetBean = null;
105:
106: private static final Bundle bundle = Bundle
107: .getBundle(BindingTargetPanel.class);
108:
109: public BindingTargetPanel() {
110: try {
111: jbInit();
112: } catch (Exception ex) {
113: ex.printStackTrace();
114: }
115: rootNode.setTreeModel(treeModel);
116: tree.getAccessibleContext().setAccessibleName(
117: bundle.getMessage("TARGET_BINDING_TREE_ACCESS_NAME"));
118: tree.getAccessibleContext().setAccessibleDescription(
119: bundle.getMessage("TARGET_BINDING_TREE_ACCESS_DESC"));
120: targetLabel.setLabelFor(tree);
121: targetLabel.setDisplayedMnemonic(bundle.getMessage(
122: "TARGET_LABEL_DISPLAYED_MNEMONIC").charAt(0));
123: }
124:
125: protected BindingTargetCallback bindingCallback;
126:
127: public BindingTargetPanel(BindingTargetCallback bindingCallback) {
128: this ();
129: this .bindingCallback = bindingCallback;
130: }
131:
132: protected DesignContext[] sortContexts(DesignContext[] contexts) {
133: ArrayList sortList = new ArrayList();
134:
135: TreeMap nameMap = new TreeMap();
136: for (int i = 0; i < contexts.length; i++) {
137: nameMap.put(contexts[i].getDisplayName(), contexts[i]);
138: }
139:
140: String[] names = (String[]) nameMap.keySet().toArray(
141: new String[nameMap.size()]);
142:
143: // request scope
144: for (int i = 0; i < names.length; i++) {
145: DesignContext c = (DesignContext) nameMap.get(names[i]);
146: if ("request".equals(c
147: .getContextData(Constants.ContextData.SCOPE))) { //NOI18N
148: sortList.add(c);
149: }
150: }
151: // session scope
152: for (int i = 0; i < names.length; i++) {
153: DesignContext c = (DesignContext) nameMap.get(names[i]);
154: if ("session".equals(c
155: .getContextData(Constants.ContextData.SCOPE))) { //NOI18N
156: sortList.add(c);
157: }
158: }
159: // application scope
160: for (int i = 0; i < names.length; i++) {
161: DesignContext c = (DesignContext) nameMap.get(names[i]);
162: if ("application".equals(c
163: .getContextData(Constants.ContextData.SCOPE))) { //NOI18N
164: sortList.add(c);
165: }
166: }
167: // none scope
168: for (int i = 0; i < names.length; i++) {
169: DesignContext c = (DesignContext) nameMap.get(names[i]);
170: if ("none".equals(c
171: .getContextData(Constants.ContextData.SCOPE))) { //NOI18N
172: sortList.add(c);
173: }
174: }
175:
176: return (DesignContext[]) sortList
177: .toArray(new DesignContext[sortList.size()]);
178: }
179:
180: protected DesignBean findUIDataParentWithVar(DesignBean fromBean) {
181: DesignBean b = fromBean.getBeanParent();
182: while (b != null && !(b.getInstance() instanceof UIData)) {
183: b = b.getBeanParent();
184: }
185: if (b != null && b.getInstance() instanceof UIData) {
186: DesignProperty varProp = b.getProperty("var"); // NOI18N
187: if (varProp != null) {
188: Object var = varProp.getValue();
189: if (var != null) {
190: return b;
191: }
192: }
193: }
194: return null;
195: }
196:
197: protected DesignContext showingContext = null;
198:
199: public void sourceContextChanged(DesignContext context) {
200: if (showingContext != null && showingContext == context)
201: return;
202: showingContext = context;
203: rootNode.removeAll();
204: rootNode.add(new Null(rootNode));
205: ArrayList expands = new ArrayList();
206: if (context != null && context.getProject() != null) {
207: //DesignContext[] acs = context.getProject().getDesignContexts();
208: DesignContext[] acs = getDesignContexts(context);
209: acs = sortContexts(acs);
210: for (int i = 0; acs != null && i < acs.length; i++) {
211:
212: //System.out.println("ADDING NEW CONTEXT: " + context.getDisplayName());
213: if ((acs[i].getBeans() != null)
214: && (acs[i].getRootContainer().getChildBeans().length > 0)) {
215: DesignBean[] dpKids = acs[i]
216: .getBeansOfType(DataProvider.class);
217: DesignBean[] rsKids = acs[i]
218: .getBeansOfType(RowSet.class);
219: // Do not show the data provider in the object binding dialog.
220: // We have explicit data provider binding dialog
221: if (acs[i].getRootContainer().getChildBeans().length > (dpKids.length + rsKids.length)) {
222: BindingTargetNode node = new ContextTargetNode(
223: rootNode, acs[i]);
224: expands.add(new TreePath(new Object[] {
225: rootNode, node }));
226: rootNode.add(node);
227: }
228: }
229: }
230: }
231: treeModel.reload();
232: tree.validate();
233: for (int i = 0; i < expands.size(); i++) {
234: tree.expandPath((TreePath) expands.get(i));
235: }
236: refreshTarget();
237: }
238:
239: // For performance improvement. No need to get all the contexts in the project
240: private DesignContext[] getDesignContexts(DesignContext context) {
241: DesignProject designProject = context.getProject();
242: DesignContext[] contexts;
243: if (designProject instanceof FacesDesignProject) {
244: contexts = ((FacesDesignProject) designProject)
245: .findDesignContexts(new String[] { "request",
246: "session", "application" });
247: } else {
248: contexts = new DesignContext[0];
249: }
250: DesignContext[] designContexts = new DesignContext[contexts.length + 1];
251: designContexts[0] = context;
252: System.arraycopy(contexts, 0, designContexts, 1,
253: contexts.length);
254: return designContexts;
255: }
256:
257: protected UIDataVarNode varNode = null;
258:
259: protected DesignBean showingBean = null;
260:
261: public void sourceBeanChanged(DesignBean bean) {
262: if (showingBean == bean)
263: return;
264: showingBean = bean;
265: if (varNode != null) {
266: rootNode.remove(varNode);
267: treeModel.reload();
268: tree.validate();
269: varNode = null;
270: }
271: if (showingBean != null) {
272: DesignBean uiDataBean = findUIDataParentWithVar(showingBean);
273: if (uiDataBean != null) {
274: varNode = new UIDataVarNode(rootNode, uiDataBean);
275: rootNode.add(1, varNode);
276: treeModel.reload();
277: tree.validate();
278: }
279: }
280: refreshTarget();
281: }
282:
283: protected DesignProperty showingProp = null;
284:
285: public void sourcePropertyChanged(DesignProperty prop) {
286: if (showingProp == prop)
287: return;
288: showingProp = prop;
289: refreshTarget();
290: }
291:
292: public void refreshTarget() {
293: if (showingProp != null) {
294: String vx = showingProp.getValueSource();
295: boolean bound = vx != null && vx.startsWith("#{")
296: && vx.endsWith("}"); //NOI18N
297: if (bound) {
298: selectNodeForExpression(vx
299: .substring(2, vx.length() - 1));
300: } else {
301: tree.setSelectionRow(0);
302: }
303: } else {
304: tree.setSelectionRow(0);
305: }
306: repaint(100);
307: }
308:
309: TreeNode findChildNodeForExprPart(TreeNode node, String exprPart) {
310: Enumeration e = node.children();
311: while (e.hasMoreElements()) {
312: TreeNode n = (TreeNode) e.nextElement();
313: if (n instanceof BindingTargetNode) {
314: BindingTargetNode btn = (BindingTargetNode) n;
315: if (exprPart.equals(btn.getBindingExpressionPart())) {
316: return n;
317: }
318: }
319: }
320: return null;
321: }
322:
323: void selectNodeForExpression(String expr) {
324: //System.out.println("expr="+expr);
325: StringTokenizer st = new StringTokenizer(expr, ".");
326: ArrayList parts = new ArrayList();
327: while (st.hasMoreElements()) {
328: parts.add(st.nextElement());
329: //System.out.println(" part: " + parts.get(parts.size() - 1));
330: }
331: TreeNode node = rootNode;
332: TreeNode lastNode = node;
333: while (parts.size() > 0) {
334: node = findChildNodeForExprPart(lastNode, (String) parts
335: .get(0));
336: if (node != null) {
337: parts.remove(0);
338: lastNode = node;
339: } else {
340: break;
341: }
342: }
343: if (lastNode != null && lastNode != rootNode) {
344: tree.setSelectionPath(createPath(lastNode));
345: } else {
346: tree.setSelectionRow(0);
347: }
348: }
349:
350: TreePath createPath(TreeNode node) {
351: ArrayList path = new ArrayList();
352: while (node != null) {
353: path.add(0, node);
354: node = node.getParent();
355: }
356: return new TreePath((TreeNode[]) path.toArray(new TreeNode[path
357: .size()]));
358: }
359:
360: ActionListener updateCallback = new ActionListener() {
361: public void actionPerformed(ActionEvent e) {
362: refreshExpr();
363: }
364: };
365:
366: String calcExpression(BindingTargetNode targetNode) {
367: targetBean = null;
368: ArrayList parts = new ArrayList();
369: TreeNode n = targetNode;
370: while (n != null) {
371: if (n instanceof PropertyTargetNode) {
372: PropertyTargetNode ptn = (PropertyTargetNode) n;
373: if (ptn.isValidBindingTarget()) {
374: PropertyDescriptor[] propPath = ptn.getPropPath();
375: if (propPath != null) {
376: for (int i = propPath.length - 1; i >= 0; i--) {
377: parts.add(0, propPath[i].getName());
378: }
379: }
380: targetBean = ptn.getBean();
381: parts.add(0, ptn.getBean().getInstanceName());
382: DesignContext c = ptn.getBean().getDesignContext();
383: if (c instanceof FacesDesignContext) {
384: parts.add(0, ((FacesDesignContext) c)
385: .getReferenceName());
386: } else {
387: parts.add(0, c.getDisplayName());
388: }
389: break;
390: }
391: } else if (n instanceof BindingTargetNode) {
392: BindingTargetNode btn = (BindingTargetNode) n;
393: if (btn.isValidBindingTarget()) {
394: String ep = btn.getBindingExpressionPart();
395: if (ep != null) {
396: //MBOHM fix 5086833
397: //escape single quotes within selectItems[' ... '], say, for selectItems['personid || \'-\' }} jobtitle']
398: String siStart = "selectItems['"; //NOI18N
399: String siEnd = "']"; //NOI18N
400: if (ep.startsWith(siStart)
401: && ep.endsWith(siEnd)
402: && ep.length() > siStart.length()
403: + siEnd.length()) {
404: String epCrux = ep.substring(siStart
405: .length(), ep.length()
406: - siEnd.length());
407: epCrux = epCrux.replaceAll("\\'", "\\\\'");
408: ep = siStart + epCrux + siEnd;
409: }
410: parts.add(0, ep);
411: }
412: }
413: }
414: n = n.getParent();
415: }
416: if (parts.size() > 0) {
417: StringBuffer expr = new StringBuffer();
418: expr.append("#{"); // NOI18N
419: for (int i = 0; i < parts.size(); i++) {
420: String part = "" + parts.get(i);
421: if (i > 0 && !part.startsWith("[")) { // NOI18N
422: expr.append("."); // NOI18N
423: }
424: expr.append(part);
425: }
426: expr.append("}"); // NOI18N
427: return expr.toString();
428: }
429: return "";
430: }
431:
432: JComponent customPanel = null;
433: boolean needsRefresh = false;
434:
435: void refreshExpr() {
436: if (customPanel != null) {
437: this .remove(customPanel);
438: customPanel = null;
439: needsRefresh = true;
440: }
441: TreePath tp = tree.getSelectionPath();
442: if (tp != null) {
443: Object o = tp.getLastPathComponent();
444: if (o instanceof BindingTargetNode) {
445: BindingTargetNode btn = (BindingTargetNode) o;
446: if (!(btn instanceof Null)) {
447: bindingCallback
448: .setNewExpressionText(calcExpression(btn));
449: }
450: customPanel = btn.getCustomDisplayPanel(updateCallback);
451: if (customPanel != null) {
452: this .add(customPanel, customPanelConstraints);
453: needsRefresh = true;
454: }
455: } else {
456: bindingCallback.setNewExpressionText(""); //NOI18N
457: }
458: } else {
459: bindingCallback.setNewExpressionText(""); //NOI18N
460: }
461: if (needsRefresh) {
462: bindingCallback.refresh();
463: }
464: }
465:
466: public DesignBean getTargetBean() {
467: return targetBean;
468: }
469:
470: static {
471: //!JOE HACK HACK HACK! I don't have a static 'hook' to register node factories
472: BindingTargetNode
473: ._registerBindingTargetNodeFactory(new ResultSetTargetNodeFactory());
474: BindingTargetNode
475: ._registerBindingTargetNodeFactory(new MapTargetNodeFactory());
476: BindingTargetNode
477: ._registerBindingTargetNodeFactory(new DataProviderTargetNodeFactory());
478: }
479:
480: void jbInit() throws Exception {
481:
482: targetLabel.setText(bundle.getMessage("selectTarget")); //NOI18N
483: targetLabel.setDisplayedMnemonic(bundle.getMessage(
484: "selectTargeDisplayedMnemonic").charAt(0)); //NOI18N
485: tree.setModel(treeModel);
486: tree.setEditable(false);
487: tree.setShowsRootHandles(true);
488: tree.setRootVisible(false);
489: //tree.setLargeModel(true);
490: tree.setCellRenderer(new TargetTreeRenderer());
491: tree.setSelectionModel(treeSelectionModel);
492: treeSelectionModel
493: .setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
494: treeSelectionModel
495: .addTreeSelectionListener(new TreeSelectionListener() {
496: public void valueChanged(TreeSelectionEvent e) {
497: refreshExpr();
498: }
499: });
500: treeModel.addTreeModelListener(new TreeModelListener() {
501: public void treeNodesChanged(TreeModelEvent e) {
502: updateTreePainting();
503: }
504:
505: public void treeNodesInserted(TreeModelEvent e) {
506: updateTreePainting();
507: }
508:
509: public void treeNodesRemoved(TreeModelEvent e) {
510: updateTreePainting();
511: }
512:
513: public void treeStructureChanged(TreeModelEvent e) {
514: updateTreePainting();
515: }
516: });
517: this .setLayout(gridBagLayout1);
518: this .add(targetLabel, new GridBagConstraints(0, 0, 1, 1, 0.0,
519: 0.0, GridBagConstraints.WEST,
520: GridBagConstraints.HORIZONTAL, new Insets(8, 8, 2, 8),
521: 0, 0));
522: this .add(targetScroll, new GridBagConstraints(0, 1, 1, 1, 1.0,
523: 1.0, GridBagConstraints.CENTER,
524: GridBagConstraints.BOTH, new Insets(0, 8, 8, 8), 0, 0));
525: targetScroll.getViewport().add(tree, null);
526: }
527:
528: private void updateTreePainting() {
529: SwingUtilities.invokeLater(new Runnable() {
530: public void run() {
531: tree.validate();
532: targetScroll.validate();
533: }
534: });
535: }
536:
537: class TargetTreeRenderer extends DefaultTreeCellRenderer {
538: public Component getTreeCellRendererComponent(JTree tree,
539: Object value, boolean sel, boolean expanded,
540: boolean leaf, int row, boolean hasFocus) {
541:
542: super .getTreeCellRendererComponent(tree, value, sel,
543: expanded, leaf, row, hasFocus);
544:
545: boolean enableNode = true;
546: if (value instanceof BindingTargetNode) {
547: BindingTargetNode btn = (BindingTargetNode) value;
548: if (showingProp != null) {
549: Class showingPropType = showingProp
550: .getPropertyDescriptor().getPropertyType();
551: if (showingPropType != null) {
552: // EAT: There has to be a better way :(
553: // Not handling Array types of these at the moment
554: if (showingPropType.isPrimitive()) {
555: if (showingPropType == Boolean.TYPE) {
556: showingPropType = Boolean.class;
557: } else if (showingPropType == Character.TYPE) {
558: showingPropType = Character.class;
559: } else if (showingPropType == Byte.TYPE) {
560: showingPropType = Byte.class;
561: } else if (showingPropType == Short.TYPE) {
562: showingPropType = Short.class;
563: } else if (showingPropType == Integer.TYPE) {
564: showingPropType = Integer.class;
565: } else if (showingPropType == Long.TYPE) {
566: showingPropType = Long.class;
567: } else if (showingPropType == Float.TYPE) {
568: showingPropType = Float.class;
569: } else if (showingPropType == Double.TYPE) {
570: showingPropType = Double.class;
571: }
572: }
573: Class tc = btn.getTargetTypeClass();
574: if (tc != null) {
575: enableNode = showingPropType
576: .isAssignableFrom(tc);
577: }
578: }
579: }
580: String customText = btn.getDisplayText(enableNode);
581: if (customText != null) {
582: this .setText(customText);
583: }
584: if (btn.hasDisplayIcon()) {
585: Icon customIcon = btn.getDisplayIcon(enableNode);
586: if (customIcon != null) {
587: this .setIcon(customIcon);
588: }
589: }
590: }
591: // String txt = getText();
592: // if (txt != null && txt.startsWith("<html>")) {
593: // this.setText(txt.substring(6));
594: // }
595: return this ;
596: }
597: }
598:
599: /**
600: * An extension of JTree that allows a selected node to remain selected even
601: * if one of its parent nodes is toggled shut. This extension is intended to
602: * fix CR 6288174.
603: */
604: static class HiddenSelectionJTree extends JTree {
605:
606: protected boolean removeDescendantSelectedPaths(TreePath path,
607: boolean includePath) {
608: return false;
609: }
610:
611: }
612:
613: public static Icon BEAN_ICON = new ImageIcon(
614: BindingTargetPanel.class.getResource("img/property.gif")); //NOI18N
615: public static Icon TAG_ICON = new ImageIcon(
616: BindingTargetPanel.class
617: .getResource("img/html_element.png")); //NOI18N
618: }
|