/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun * Microsystems, Inc. All Rights Reserved. */ package org.openide.actions; import java.util.ResourceBundle; import java.awt.Dialog; import java.awt.Window; import java.awt.Rectangle; import java.awt.event.*; import java.beans.IntrospectionException; import java.io.*; import java.lang.reflect.Method; import javax.swing.*; import org.openide.*; import org.openide.loaders.*; import org.openide.cookies.*; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileLock; import org.openide.explorer.propertysheet.*; import org.openide.util.*; import org.openide.util.actions.CookieAction; import org.openide.nodes.*; /** Customize a JavaBean. * Opens a Property Sheet and allows making a * serialized prototype from the modified object. * <p>This class is final only for performance reasons. * * @author Ian Formanek */ public final class CustomizeBeanAction extends CookieAction { /** constant for HelpCtx Customize Bean */ private static final String HELP_KEY_CUSTOMIZING = "beans.customizing"; // NOI18N /** generated Serialized Version UID */ static final long serialVersionUID = -6378495195905487716L; /* Actually performs this action */ protected void performAction (final Node[] activatedNodes) { if (compileNodes (activatedNodes)) { customize ((InstanceCookie)activatedNodes[0].getCookie(InstanceCookie.class)); } } /** Execute some data objects. * * @param nodes the array of nodes to compile * @return true if compilation succeeded or was not performed, false if compilation failed * @deprecated Use <code>AbstractCompileAction.compileNodes</code> (Compiler API) instead. */ public static boolean compileNodes(Node[] nodes) { try { Class c = ((ClassLoader)Lookup.getDefault().lookup(ClassLoader.class)).loadClass("org.openide.actions.AbstractCompileAction"); // NOI18N Method m = c.getDeclaredMethod("compileNodes", new Class[] {Node[].class}); // NOI18N return ((Boolean)m.invoke(null, new Object[] {nodes})).booleanValue(); } catch (ClassNotFoundException e) { // Failed, but not implausible. return false; } catch (Exception e) { // Something else wrong. ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); return false; } } /* Exactly one selected node. */ protected int mode () { return MODE_EXACTLY_ONE; } /* @return InstanceCookie node */ protected Class[] cookieClasses () { return new Class[] { InstanceCookie.class }; } /* Human presentable name of the action. This should be * presented as an item in a menu. * @return the name of the action */ public String getName () { return NbBundle.getMessage(CustomizeBeanAction.class, "CustomizeBean"); } /* Help context where to find more about the acion. * @return the help context for this action */ public HelpCtx getHelpCtx () { return new HelpCtx (CustomizeBeanAction.class); } /* Resource name for the icon. * @return resource name */ protected String resourceIcon () { return "/org/openide/resources/actions/customize.gif"; // NOI18N } /** Customize a Bean. * @param cookie the object which can be instantiated */ public static void customize (final InstanceCookie cookie) { if (cookie == null) return; final ResourceBundle bundle = NbBundle.getBundle(CustomizeBeanAction.class); Window w = null; // Visual rep. of bean Object b = null; // BEAN try { b = cookie.instanceCreate (); } catch (Exception ex) { ErrorManager em = ErrorManager.getDefault (); em.annotate (ex, bundle.getString ("EXC_IntrospectionNoClass")); em.notify (ErrorManager.WARNING, ex); return; } final Object bean = b; // show visual repres. of bean if (bean instanceof java.awt.Window) { w = (java.awt.Window)bean; w.addWindowListener (new java.awt.event.WindowAdapter () { public void windowClosing(java.awt.event.WindowEvent e) { e.getWindow ().dispose (); } }); } else if (bean instanceof java.awt.Component) { // JST: do not use PropertyDialogManager // just create the window with some reasonable title DialogDescriptor dd = new DialogDescriptor ( (java.awt.Component)bean, bundle.getString ("CTL_Component_Title"), false, null ); dd.setOptions(new Object[] { DialogDescriptor.CLOSED_OPTION }); dd.setHelpCtx( new HelpCtx( HELP_KEY_CUSTOMIZING )); w = DialogDisplayer.getDefault ().createDialog (dd); } final Window window = w; // create propertysheet PropertySheet propertySheet = new PropertySheet (); Node[] nodes = new Node [1]; BeanNode bn = null; try { bn = new BeanNode(bean); } catch (IntrospectionException e) { ErrorManager em = ErrorManager.getDefault (); em.annotate (e, NbBundle.getMessage ( CustomizeBeanAction.class, "EXC_Introspection", bean.getClass ().getName () )); em.notify (ErrorManager.WARNING, e); return; } nodes [0] = bn; propertySheet.setNodes (nodes); final JButton ser = new JButton (bundle.getString ("CTL_Serialize")); final JButton serAs = new JButton (bundle.getString ("CTL_SerializeAs")); final JButton cancel = new JButton (bundle.getString ("CTL_Cancel")); serAs.setMnemonic(bundle.getString ("CTL_SerializeAs_Mnemonic").charAt(0)); ser.getAccessibleContext().setAccessibleDescription(bundle.getString("ACSD_Serialize")); serAs.getAccessibleContext().setAccessibleDescription(bundle.getString("ACSD_SerializeAs")); cancel.getAccessibleContext().setAccessibleDescription(bundle.getString("ACSD_Cancel")); propertySheet.getAccessibleContext().setAccessibleDescription(bundle.getString("ACSD_CustomizeBeanPanel")); // dialog[0] = opened dialog final Dialog dialog[] = new Dialog[1]; boolean brr = java.io.Serializable.class.isAssignableFrom (bean.getClass ()); serAs.setEnabled (brr); ser.setEnabled (brr && cookie instanceof InstanceCookie.Origin); ActionListener listener = new ActionListener () { public void actionPerformed (ActionEvent ev) { if (ev.getSource () == cancel || ev.getSource() == NotifyDescriptor.CANCEL_OPTION || ev.getSource() == NotifyDescriptor.CLOSED_OPTION ) { dialog[0].dispose (); if (window != null) window.dispose (); return; } if (serializeJavaBean (bean, ev.getSource () == serAs ? null : ((InstanceCookie.Origin)cookie).instanceOrigin () )) { dialog[0].dispose (); if (window != null) window.dispose (); } } }; DialogDescriptor descr = new DialogDescriptor ( propertySheet, NbBundle.getMessage(CustomizeBeanAction.class, "FMT_CTL_CustomizeTitle", bean.getClass().getName()), false, // modal new Object[] { ser, serAs, cancel }, // options cancel, // initial value DialogDescriptor.DEFAULT_ALIGN, new HelpCtx ( HELP_KEY_CUSTOMIZING ), listener ); dialog[0] = DialogDisplayer.getDefault ().createDialog (descr); // add listener to close bean window CustomizeWindowAdapter wa = new CustomizeWindowAdapter (window); dialog[0].addWindowListener(wa); dialog[0].show (); synchronized (wa) { // monitor to CustomizeWindowAdapter.windowClosed if (window != null && ! wa.closedBeenWindow) { Rectangle r = dialog [0].getBounds (); window.setLocation (r.x + r.width, r.y); window.show (); wa.closedBeenWindow = true; } } } /** Serialize a bean to file. * * @param bean the bean to be serialized * @param file the file to serialize to, or <code>null</code> to prompt the user for a destination * @return <code>true</code> if successful */ public static boolean serializeJavaBean (final Object bean, final FileObject file) { final ResourceBundle bundle = NbBundle.getBundle(CustomizeBeanAction.class); FileObject parent = null; String name = null; org.openide.filesystems.FileSystem targetFS; try { if (file == null) { JPanel p = new JPanel(new java.awt.BorderLayout(12, 0)); JTextField tf = new JTextField (20); JLabel l = new JLabel(bundle.getString("CTL_SerializeTarget")); l.setDisplayedMnemonic(bundle.getString("CTL_SerializeTarget_Mnemonic").charAt(0)); l.setLabelFor(tf); p.add(tf, java.awt.BorderLayout.CENTER); p.add(l, java.awt.BorderLayout.WEST); tf.getAccessibleContext().setAccessibleDescription(bundle.getString("ACSD_SerializeTarget")); try { // selects one folder from data systems DataFolder df = (DataFolder)NodeOperation.getDefault().select ( bundle.getString ("CTL_SerializeAs"), bundle.getString ("CTL_SaveIn"), RepositoryNodeFactory.getDefault().repository(new FolderFilter()), new FolderAcceptor(), p )[0].getCookie(DataFolder.class); parent = df.getPrimaryFile (); targetFS = parent.getFileSystem (); name = tf.getText (); } catch (org.openide.util.UserCancelException ex) { return false; } } else { parent = file.getParent (); name = file.getName (); targetFS = file.getFileSystem (); } } catch (org.openide.filesystems.FileStateInvalidException e) { // XXX I18N violation to separate strings this way - use format instead ErrorManager.getDefault().annotate(e, bundle.getString ("EXC_Serialization") + " " + name); ErrorManager.getDefault().notify(e); return false; } final String fileName = name; final FileObject parentFile = parent; try { targetFS.runAtomicAction(new org.openide.filesystems.FileSystem.AtomicAction() { public void run() throws IOException { ByteArrayOutputStream baos = null; ObjectOutputStream oos = null; OutputStream os = null; FileObject serFile = null; FileLock lock = null; try { oos = new java.io.ObjectOutputStream (baos = new ByteArrayOutputStream ()); oos.writeObject (bean); if ((serFile = parentFile.getFileObject (fileName, "ser")) == null) // NOI18N serFile = parentFile.createData (fileName, "ser"); // NOI18N lock = serFile.lock (); oos.close (); baos.writeTo (os = serFile.getOutputStream (lock)); } finally { if (lock != null) lock.releaseLock (); if (os != null) os.close (); } } } ); } catch (Exception e) { ErrorManager.getDefault().annotate(e, NbBundle.getMessage(CustomizeBeanAction.class, "EXC_Serialization2", bean.getClass().getName())); ErrorManager.getDefault().notify(ErrorManager.EXCEPTION, e); return false; } return true; } /** Filter for save as operation, accepts folders. */ private static final class FolderFilter implements DataFilter { static final long serialVersionUID =6754682007992329276L; FolderFilter() {} /** Accepts only data folders but ignore read only roots of file systems */ public boolean acceptDataObject (DataObject obj) { return obj instanceof DataFolder && (!obj.getPrimaryFile ().isReadOnly () || obj.getPrimaryFile ().getParent () != null); } } // end of FolderFilter inner class /** Node acceptor that accepts read-write folders only */ private static final class FolderAcceptor implements NodeAcceptor { FolderAcceptor() {} public boolean acceptNodes (Node[] nodes) { if ((nodes == null) || (nodes.length == 0)) return false; DataFolder cookie = (DataFolder)nodes[0].getCookie (DataFolder.class); return nodes.length == 1 && cookie != null && !cookie.getPrimaryFile().isReadOnly(); } } // end of FolderAcceptor inner class /** Window adapter for customize dialog. */ private static class CustomizeWindowAdapter extends WindowAdapter{ /** when been is instance of window it detect its closing */ public boolean closedBeenWindow = false; /** window of beans */ private Window window; /** @param window is window of been.*/ public CustomizeWindowAdapter (Window window) { this.window = window; } /** Customize dialog closed. */ public void windowClosed (WindowEvent we) { synchronized (this) { // monitor to CustomizeBeanAction.customize if (window != null ) { window.dispose(); closedBeenWindow = true; } } } } }