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