001: /*
002: * $Id: ObjectPanel.java 6115 2006-01-30 04:50:47Z dfs $
003: *
004: * Copyright 2004-2006 Daniel F. Savarese
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.savarese.org/software/ApacheLicense-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: package org.savarese.unicorn.ui;
020:
021: import java.awt.*;
022: import java.awt.event.*;
023: import java.lang.reflect.*;
024: import java.util.*;
025:
026: import javax.swing.*;
027: import javax.swing.border.*;
028: import javax.swing.text.*;
029:
030: import javax.management.*;
031:
032: import org.savarese.unicorn.ui.event.*;
033: import org.savarese.unicorn.util.Sorting;
034: import org.savarese.unicorn.util.Types;
035:
036: /**
037: * A Swing component that dynamically assembles a user interface for
038: * invoking the methods of an object wrapped by an {@link
039: * ObjectPanelModel} implementation.
040: */
041: public class ObjectPanel<O extends Object> extends JPanel {
042:
043: // private static final Color __ATTRIBUTE_COLOR = new Color(60, 70, 116);
044: private static final Color __ATTRIBUTE_COLOR = new Color(35, 70, 8);
045: private static final Color __METHOD_COLOR = new Color(107, 2, 4);
046:
047: private ObjectPanelModel<O> __model;
048: private ObjectPanelModelListener<O> __modelListener;
049: private Comparator<MBeanAttributeInfo> __attributeComparator;
050: private Comparator<MBeanConstructorInfo> __constructorComparator;
051: private Comparator<MBeanOperationInfo> __operationComparator;
052: private Border __originalBorder;
053: private ArrayList<Attribute> __attributes;
054: private ActionListener __refreshListener;
055:
056: class Attribute implements ActionListener {
057: String _name;
058: Class _type;
059: JTextComponent _field;
060:
061: Attribute(String name, String type, JTextComponent field)
062: throws ClassNotFoundException {
063: _name = name;
064: _field = field;
065: _type = Types.getClass(type);
066: }
067:
068: void refresh() {
069: Object value = null;
070:
071: try {
072: value = __model.getAttribute(_name);
073: } catch (Exception e) {
074: value = "Error: could not be obtained: "
075: + e.getMessage();
076: }
077:
078: _field.setText(Types.convert(value));
079: }
080:
081: public void actionPerformed(ActionEvent event) {
082: try {
083: __model.setAttribute(_name, Types.convert(_field
084: .getText(), _type));
085: } catch (Exception e) {
086: // TODO: Should log this.
087: throw new RuntimeException(e);
088: }
089: }
090: }
091:
092: class InvocationAction extends AbstractAction {
093: private String __method;
094: private String[] __typeNames;
095: private Class[] __types;
096: private JTextComponent[] __args;
097: private JTextArea __resultArea;
098:
099: private void invoke() {
100: Object[] values = new Object[__args.length];
101:
102: try {
103: for (int i = 0; i < values.length; ++i)
104: values[i] = Types.convert(__args[i].getText(),
105: __types[i]);
106:
107: Object result = __model.invoke(__method, values,
108: __types, __typeNames);
109:
110: if (__resultArea != null)
111: __resultArea.setText(Types.convert(result));
112: } catch (Exception e) {
113: // TODO: Should really log and open up an error dialog
114: throw new RuntimeException(e);
115: }
116: }
117:
118: public InvocationAction(String name, String method,
119: JTextComponent[] args, String[] typeNames,
120: Class[] argTypes)
121:
122: {
123: super (name);
124: __method = method;
125: __args = args;
126: __types = argTypes;
127: __typeNames = typeNames;
128: __resultArea = null;
129: }
130:
131: public void setResultArea(JTextArea area) {
132: __resultArea = area;
133: }
134:
135: public void actionPerformed(ActionEvent e) {
136: invoke();
137: }
138: }
139:
140: private JPanel __makeFieldPanel(String title, JTextComponent field,
141: boolean editable, Color titleColor) {
142: JPanel panel = Util._makePanel(title, titleColor);
143:
144: field.setBackground(Color.WHITE);
145: field.setEditable(editable);
146: field.setAlignmentX(Component.LEFT_ALIGNMENT);
147:
148: if (field instanceof JTextField) {
149: Dimension max = field.getMaximumSize();
150: max.height = field.getFontMetrics(field.getFont())
151: .getHeight();
152: max.height += (max.height >> 2);
153: field.setMaximumSize(max);
154: Dimension pref = field.getPreferredSize();
155: pref.height = max.height;
156: field.setPreferredSize(pref);
157: panel.add(field);
158: } else if (field instanceof JTextArea) {
159: JTextArea area = (JTextArea) field;
160: JScrollPane pane = new JScrollPane(area);
161: area.setColumns(20);
162: area.setRows(4);
163: pane.setAlignmentX(Component.LEFT_ALIGNMENT);
164: panel.add(pane);
165: }
166:
167: return panel;
168: }
169:
170: private JPanel __makeFieldPanel(String title, JTextComponent field,
171: boolean editable) {
172: return __makeFieldPanel(title, field, editable, Color.BLACK);
173: }
174:
175: private JPanel __makeLabelPanel(String title, String text) {
176: JPanel panel = Util._makePanel(title);
177: JLabel label = new JLabel(text);
178:
179: label.setAlignmentX(Component.LEFT_ALIGNMENT);
180:
181: panel.add(label);
182:
183: return panel;
184: }
185:
186: private void __refreshProperties() {
187: for (Attribute a : __attributes)
188: a.refresh();
189: }
190:
191: private void __addHeader(String name, MBeanInfo info) {
192: TitledBorder border = BorderFactory
193: .createTitledBorder(BorderFactory
194: .createEtchedBorder(EtchedBorder.LOWERED));
195: border.setTitle(name);
196: setBorder(border);
197:
198: add(__makeLabelPanel("Class", info.getClassName()));
199: add(__makeLabelPanel("Description", info.getDescription()));
200: }
201:
202: private void __addAttribute(JPanel panel, MBeanAttributeInfo info) {
203: Object value;
204: String access;
205: JPanel attributePanel;
206: String name = info.getName();
207: String type = info.getType();
208: boolean editable = info.isWritable();
209: JTextComponent attributeField = null;
210: Attribute attribute = null;
211:
212: try {
213: // TODO: Batch all attributes with a call to
214: // MBeanServerConnection.getAttributes
215: value = __model.getAttribute(name);
216:
217: if (value instanceof Object[]) {
218: JTextArea area = new JTextArea();
219: area.setLineWrap(true);
220: attributeField = area;
221: } else
222: attributeField = new JTextField();
223:
224: if (editable || info.isReadable())
225: attribute = new Attribute(name, type, attributeField);
226:
227: if (info.isReadable())
228: __attributes.add(attribute);
229:
230: } catch (Exception e) {
231: value = "Error: could not be obtained: " + e.getMessage();
232: editable = false;
233: } finally {
234: if (attributeField == null)
235: attributeField = new JTextField();
236: }
237:
238: // We don't consider the non-readable/non-writable case because
239: // then it's not an attribute
240: if (info.isReadable() && !info.isWritable())
241: access = "Read Only";
242: else if (!info.isReadable() && info.isWritable())
243: access = "Write Only";
244: else
245: access = "Read/Write";
246:
247: attributeField.setText(Types.convert(value));
248:
249: attributePanel = __makeFieldPanel(name + " : " + type + " : "
250: + access, attributeField, editable, __ATTRIBUTE_COLOR);
251:
252: if (editable) {
253: ChainedActionListener listener = new ChainedActionListener();
254: listener.addEventListener(attribute);
255: listener.addEventListener(__refreshListener);
256:
257: if (attributeField instanceof JTextField)
258: ((JTextField) attributeField)
259: .addActionListener(listener);
260: else {
261: JButton button = new JButton("Set");
262: button.addActionListener(listener);
263: panel.add(button);
264: }
265: }
266:
267: panel.add(attributePanel);
268: }
269:
270: private void __addMethod(JPanel panel, String name, String retType,
271: String description, MBeanParameterInfo[] signature,
272: boolean invokable) {
273: JPanel methodPanel;
274: String method = new String(name);
275: String[] typeNames = new String[signature.length];
276: Class[] types = new Class[signature.length];
277: JTextComponent[] fields = new JTextComponent[signature.length];
278: boolean error = false;
279: String errorMessage = "";
280: int i;
281:
282: name += "(";
283:
284: if (signature.length > 0) {
285: i = 0;
286: while (true) {
287: name += signature[i].getType();
288: if (++i >= signature.length)
289: break;
290: name += ",";
291: }
292: }
293:
294: name += ")";
295:
296: if (retType == null)
297: methodPanel = Util._makePanel(name, __METHOD_COLOR);
298: else
299: methodPanel = Util._makePanel(retType + " " + name,
300: __METHOD_COLOR);
301:
302: JPanel descriptionPanel = __makeLabelPanel("Description",
303: description);
304: methodPanel.add(descriptionPanel);
305:
306: if (signature.length > 0) {
307: JPanel paramPanel = Util._makePanel("Parameters");
308:
309: for (i = 0; i < signature.length; i++) {
310: String type = signature[i].getType();
311: String title = "(" + type + ")"
312: + signature[i].getName();
313:
314: typeNames[i] = type;
315:
316: try {
317: types[i] = Types.getClass(type);
318: } catch (ClassNotFoundException cnfe) {
319: // TODO: log this
320: cnfe.printStackTrace();
321: errorMessage += cnfe.getMessage();
322: error = true;
323: }
324:
325: if (types[i].isArray()) {
326: JTextArea area = new JTextArea();
327: area.setLineWrap(true);
328: fields[i] = area;
329: } else
330: fields[i] = new JTextField("");
331:
332: JPanel p = __makeFieldPanel(title, fields[i], invokable);
333:
334: paramPanel.add(p);
335: }
336:
337: methodPanel.add(paramPanel);
338: }
339:
340: if (invokable) {
341: if (!error) {
342: InvocationAction action = new InvocationAction(
343: "Invoke", method, fields, typeNames, types);
344: JButton button = new JButton("Invoke");
345: ChainedActionListener listener = new ChainedActionListener();
346:
347: listener.addEventListener(action);
348: listener.addEventListener(__refreshListener);
349: button.addActionListener(listener);
350: methodPanel.add(button);
351:
352: // Create area to display result
353: if (!"void".equals(retType)) {
354: JTextArea area = new JTextArea();
355: area.setLineWrap(true);
356: action.setResultArea(area);
357: methodPanel.add(__makeFieldPanel("Result", area,
358: false));
359: }
360: } else {
361: String message = "Invocation information could not be collected: "
362: + errorMessage;
363: JLabel label = new JLabel(message);
364: label.setForeground(Color.RED);
365: methodPanel.add(label);
366: }
367: }
368:
369: panel.add(methodPanel);
370: }
371:
372: private void __addConstructor(JPanel panel,
373: MBeanConstructorInfo info) {
374: __addMethod(panel, info.getName(), null, info.getDescription(),
375: info.getSignature(), false);
376: }
377:
378: private void __addOperation(JPanel panel, MBeanOperationInfo info) {
379: __addMethod(panel, info.getName(), info.getReturnType(), info
380: .getDescription(), info.getSignature(), true);
381: }
382:
383: private void __addAttributes(MBeanAttributeInfo[] attributes) {
384: JPanel panel = Util._makePanel("Attributes");
385:
386: Arrays.sort(attributes, __attributeComparator);
387:
388: for (int i = 0; i < attributes.length; ++i)
389: __addAttribute(panel, attributes[i]);
390:
391: add(panel);
392: }
393:
394: private void __addConstructors(MBeanConstructorInfo[] constructors) {
395: JPanel panel = Util._makePanel("Constructors");
396:
397: Arrays.sort(constructors, __constructorComparator);
398:
399: for (int i = 0; i < constructors.length; ++i)
400: __addConstructor(panel, constructors[i]);
401:
402: add(panel);
403: }
404:
405: private void __addOperations(MBeanOperationInfo[] operations) {
406: JPanel panel = Util._makePanel("Operations");
407:
408: Arrays.sort(operations, __operationComparator);
409:
410: for (int i = 0; i < operations.length; ++i)
411: __addOperation(panel, operations[i]);
412:
413: add(panel);
414: }
415:
416: private void __assembleComponents() {
417: __attributes.clear();
418: removeAll();
419: setBorder(__originalBorder);
420:
421: if (__model == null || !__model.isValid())
422: return;
423:
424: MBeanInfo info;
425:
426: info = __model.getObjectInfo();
427:
428: __addHeader(__model.getObjectName(), info);
429: __addAttributes(info.getAttributes());
430: __addConstructors(info.getConstructors());
431: __addOperations(info.getOperations());
432: }
433:
434: /**
435: * Instantiates an ObjectPanel backed by the specified
436: * {@link ObjectPanelModel}.
437: *
438: * @param model The {@link ObjectPanelModel} to back the ObjectPanel view.
439: */
440: public ObjectPanel(ObjectPanelModel<O> model) {
441: __attributeComparator = new Sorting.MBeanAttributeInfoComparator();
442: __constructorComparator = new Sorting.MBeanConstructorInfoComparator();
443: __operationComparator = new Sorting.MBeanOperationInfoComparator();
444: __originalBorder = getBorder();
445:
446: __attributes = new ArrayList<Attribute>();
447:
448: __refreshListener = new ActionListener() {
449: public void actionPerformed(ActionEvent e) {
450: __refreshProperties();
451: }
452: };
453:
454: __modelListener = new ObjectPanelModelListener<O>() {
455: public void modelChanged(ObjectPanelModel<O> model) {
456: __assembleComponents();
457: revalidate();
458: repaint();
459: }
460: };
461:
462: setLayout(new BoxLayout(this , BoxLayout.PAGE_AXIS));
463: setModel(model);
464: }
465:
466: /**
467: * Sets the {@link ObjectPanelModel} to back the ObjectPanel view.
468: *
469: * @param model The {@link ObjectPanelModel} to back the ObjectPanel view.
470: */
471: public void setModel(ObjectPanelModel<O> model) {
472: if (__model != null)
473: __model.removeObjectPanelModelListener(__modelListener);
474:
475: __model = model;
476:
477: if (__model != null)
478: __model.addObjectPanelModelListener(__modelListener);
479:
480: __assembleComponents();
481: }
482:
483: /**
484: * Returns the {@link ObjectPanelModel} that backs the ObjectPanel view.
485: *
486: * @return The {@link ObjectPanelModel} that backs the ObjectPanel view.
487: */
488: public ObjectPanelModel<O> getModel() {
489: return __model;
490: }
491:
492: }
|