alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 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.netbeans.modules.xml.tree.editor;

import java.awt.*;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.border.*;
import java.beans.*;
import java.io.*;
import java.io.ObjectStreamException;
import java.util.*;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;


import org.openide.DialogDescriptor;
import org.openide.NotifyDescriptor;
import org.openide.windows.TopComponent;
import org.openide.windows.Mode;
import org.openide.windows.CloneableTopComponent;
import org.openide.text.CloneableEditorSupport;
import org.openide.windows.Workspace;
import org.openide.windows.WindowManager;
import org.openide.explorer.ExplorerManager;
import org.openide.explorer.ExplorerPanel;
import org.openide.explorer.view.BeanTreeView;
import org.openide.nodes.*;
import org.openide.loaders.DataObject;
import org.openide.cookies.SaveCookie;
import org.openide.cookies.OpenCookie;
import org.openide.actions.*;
import org.openide.util.actions.*;
import org.openide.util.Utilities;

import org.netbeans.tax.*;
import org.netbeans.modules.xml.core.XMLDataObject;
import org.netbeans.modules.xml.core.XMLDataObjectLook;
import org.netbeans.modules.xml.tree.nodes.*;
import org.netbeans.modules.xml.tree.settings.TreeEditSettings;
import org.openide.util.WeakListener;


/**
 * Class TreeEditorComponent ...
 *
 * @author  Libor Kramolis
 * @version 0.1
 */
public class TreeEditorComponent extends CloneableTopComponent {
    /** Serial Version UID */
    private static final long serialVersionUID =8465527461926411443L;    

    /** */
    private transient TreeEditorSupport support;

    /** */
    private static final String MODIFIED_APPENDIX = " *"; // NOI18N

    /** */
    private static final String XML_EDITOR_MODE_BADGE_ICON = "org/netbeans/modules/xml/tree/resources/penbadge.gif"; // NOI18N
    
    /** A icon cache only. */
    private transient Image mergeIcon;
    
    /** */
    private transient LocalExplorerPanel explorerPanel;

    /** Trap -- to find wrong de-externalization. */
    private transient Exception trapExc;
    /** Trap -- to find wrong de-externalization. */
    private transient boolean initDone;

    // communicate init() -> open()
    private transient BeanTreeView treeView;

    // root context node
    private transient Node rootNode;

    
    //
    // init
    //
    
    public TreeEditorComponent () {
        // externalization requires it

        trapExc = new RuntimeException ("Public constructor of TreeEditorComponent should be called while de-externalization only!!!\nSee http://www.netbeans.org/issues/show_bug.cgi?id=19855 for current status.\nIf you know steps to reproduce it, please add them to the issue report. Thanks a lot."); // NOI18N
        initDone = false;
        if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("Init TreeEditorComponent -- default constructor!!!", trapExc); // NOI18N
    }

    /**
     * Creates new class TreeEditorComponent.
     */
    public TreeEditorComponent (TreeEditorSupport support) {
        this.support = support;

        init();
        dockIntoEditorMode();
    }


    /**
     */
    private void init () {
        
        //#25368 there is memory leak in window system for default persistence type
        // anyway remember position only for opened components
        putClientProperty("PersistenceType", "OnlyOpened");                     // NOI18N
        
        XMLDataObjectLook xmlDO = getXMLDataObjectLook();
        rootNode = new TreeEditorNode (xmlDO.getNodeDelegate());

        InnerListener listener = new InnerListener();

        // weak listener because lifetime of DO is longer
        xmlDO.addPropertyChangeListener(WeakListener.propertyChange(listener, xmlDO));
        rootNode.addPropertyChangeListener(listener);
        rootNode.addNodeListener(listener);

        explorerPanel = new LocalExplorerPanel();

        JSplitPane splittedPanel = new JSplitPane();
        JPanel treePanel = new JPanel();
        treePanel.setLayout (new BorderLayout());
        treeView = new BeanTreeView();
        treeView.addPropertyChangeListener (listener);
       
        CustomizerView customizerView = new CustomizerView();
        treePanel.add (treeView, BorderLayout.CENTER);
        splittedPanel.setLeftComponent (treePanel);
        splittedPanel.setRightComponent (customizerView);

        explorerPanel.setLayout (new BorderLayout());
        explorerPanel.add (splittedPanel, BorderLayout.CENTER);

        this.setLayout (new BorderLayout());
        this.add (explorerPanel, BorderLayout.CENTER);

        ExplorerManager explorerManager = explorerPanel.getExplorerManager();
        explorerManager.setRootContext (rootNode);
        explorerManager.addPropertyChangeListener (listener);

        TopComponent.Registry reg = explorerPanel.getRegistry();
        reg.addPropertyChangeListener (WeakListener.propertyChange(listener, reg));

        updateIcon();

        updateName();
        
        // Trap -- to find wrong de-externalization.
        initDone = true; // debug
    }


    public void open() {
        super.open();

        if ( treeView != null ) {
            try {
                int recursionLevel = TreeEditSettings.getDefault().getTreeExpansionLevel();
                expandNode(rootNode, recursionLevel);
            }
            catch (Exception ex ) { 
                // okay, _don't_ expand then
                Util.THIS.debug("Ignoring ex. while auto-expanding XML editor.", ex);  // NOI18N
            }
        }

        try {
            explorerPanel.getExplorerManager().setSelectedNodes (new Node [] { rootNode } );
        } catch (PropertyVetoException e) { 
            // ignore it
        }
    }

    private void expandNode(Node n, int recursionLevel) {
        if( recursionLevel-- <= 0 || n.isLeaf() ) return;

        treeView.expandNode(n);
        Children ch = n.getChildren();
        Enumeration nodes = ch.nodes();

        while (nodes.hasMoreElements()) {
            Node node = (Node) nodes.nextElement();
            expandNode(node, recursionLevel);            
        }
    }
    
    /**
     */
    private void dockIntoEditorMode () {
    // dock into editor mode if possible
        Workspace current = WindowManager.getDefault().getCurrentWorkspace();
        Mode editorMode = current.findMode (CloneableEditorSupport.EDITOR_MODE);
        if ( editorMode != null ) {
            editorMode.dockInto (this);
        }
    }


    /**
     */
    private XMLDataObjectLook getXMLDataObjectLook () {
        return support.getXMLDataObjectLook();
    }

    
    /** Updates the name of this top component according to
     * the existence of the save cookie in ascoiated data object
     */
    protected void updateName () {
        String name = rootNode.getDisplayName();
        if (getXMLDataObjectLook().isModified())
            setName (name + MODIFIED_APPENDIX);
        else
            setName (name);
    }


    //
    // close
    //

    /** When closing last view, also close the document.
     * @return true if close succeeded
     */
    protected boolean closeLast () {
        testTrap ("closeLast"); // NOI18N

        if (!!! support.canClose()) {
            // if we cannot close the last window
            return false;
        }
        // close everything and do not ask
        support.notifyClosed();

        return true;
    }

    
    //
    // icon
    //

    /**
     */
    private void updateIcon () {
        setIcon (createIcon());
    }

    /**
     */
    private Image createIcon () {
        if ( mergeIcon == null ) {
            mergeIcon = Utilities.loadImage (XML_EDITOR_MODE_BADGE_ICON);
        }
        return Utilities.mergeImages (rootNode.getIcon (BeanInfo.ICON_COLOR_16x16), mergeIcon, 0, 0);
    }


    /** Does nothing - overriden to keep the title unchanged */
    protected void updateTitle () {
        //empty body is right -> to eliminate super-code
    }


    private void testTrap (String context) {
        // Trap -- to find wrong de-externalization.
        if ( initDone == false ) { // assert
            Util.THIS.getErrorManager().log
                (org.openide.ErrorManager.WARNING,
                 "TreeEditorSupport." + context + ": It looks somebody call just default constructor and \"forgot\" init(). Who was that?"); // NOI18N
            Util.THIS.getErrorManager().notify (org.openide.ErrorManager.WARNING, trapExc);
        }
    }


    /**
     */
    protected void componentActivated () {
        testTrap ("componentActivated"); // NOI18N

        super.componentActivated();
        explorerPanel.componentActivated();
    }

    /**
     */
    protected void componentDeactivated () {
        testTrap ("componentDeactivated"); // NOI18N

        super.componentDeactivated();
        explorerPanel.componentDeactivated();
    }

    /**
     * Test top component validity. For invalid ones return null.
     */
    protected Object writeReplace() throws ObjectStreamException {

        //#19687 it should be invariant!
        if (support == null) {
            return null;       
        }

        // #24350 do not write down data for invalid data objects
        DataObject dataObject = support.getDataObject();
        if (dataObject.isValid() == false) {
            if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("Skipping serialization of " + dataObject); // NOI18N
            return null;
        }

        return super.writeReplace();
    }
    
    /** Serialize this top component.
     * Subclasses wishing to store state must call the super method, then write to the stream.
     * @param out the stream to serialize to
     */
    public void writeExternal (ObjectOutput out) throws IOException {
        if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("TreeEditorComponent.writeExternal()"); // NOI18N
        
        super.writeExternal (out);        
        out.writeObject (support.getEnv());
        
        if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("\tdone."); // NOI18N
    }

    /** Deserialize this top component.
     * Subclasses wishing to store state must call the super method, then read from the stream.
     * @param in the stream to deserialize from
     */
    public void readExternal (ObjectInput in) throws IOException, ClassNotFoundException {
        if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("TreeEditorComponent.readExternal();"); // NOI18N

        super.readExternal (in);

        try {
            TreeEditorSupport.Env env = (TreeEditorSupport.Env) in.readObject();
            support = env.findTreeEditorSupport();
        } catch (OptionalDataException exc) { //!!! is should be invariant (see writeExternal)
            if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("readExternal: OptionalDataException [" + exc.eof + "]", exc); // NOI18N

            if ( exc.eof == true ) { // caused because 'out.writeObject (support.getEnv());' was not called
                return;              // #20487
            } else {
                throw exc;
            }
        } catch (ClassCastException exc) { // env.findTreeEditorSupport(); // #20145
            if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("readExternal: ClassCastException", exc); // NOI18N

            return;
        }

        if (!!! ((DataObject)getXMLDataObjectLook()).isValid() ) {
            if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("readExternal: DO.is NOT Valid: " + getXMLDataObjectLook()); // NOI18N

            return;
        }

        init ();

        if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("TreeEditorComponent.readExternal(); --> The ENDE!!!"); // NOI18N
    }

    /** Resolve to original top component instance */
    private Object readResolve () throws ObjectStreamException {
        if ( Util.THIS.isLoggable() ) /* then */ {
            Util.THIS.debug ("TreeEditorComponent.readResolve();"); // NOI18N
            Util.THIS.debug ("    support = " + support); // NOI18N
        }
        
        // no support no TreeEditorComponent
        if (support == null) return null;
        
        if ( Util.THIS.isLoggable() ) /* then */ {
            Util.THIS.debug ("    DO = " + getXMLDataObjectLook()); // NOI18N
        }
        if (getXMLDataObjectLook() == null) {
            return null;
        }

        if (!!! ((DataObject)getXMLDataObjectLook()).isValid() ) { // asociated DataObject is not valid
            return null;
        }

        return this; // successfully deserialized
    }


    //
    // class InnerListener
    //

    /**
     *
     */
    private class InnerListener extends NodeAdapter implements PropertyChangeListener {

        public void propertyChange (PropertyChangeEvent ev) {
            if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("\nTreeEditorComponent::InnerListener::propertyChange: property name = " + ev.getPropertyName()); // NOI18N

            if (ExplorerManager.PROP_SELECTED_NODES.equals (ev.getPropertyName())) {

            setActivatedNodes (((ExplorerManager)ev.getSource()).getSelectedNodes());
                explorerPanel.setActivatedNodes (((ExplorerManager)ev.getSource()).getSelectedNodes ());

            } else if (TopComponent.Registry.PROP_ACTIVATED_NODES.equals (ev.getPropertyName())) {
                if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("                   ::InnerListener::propertyChange: TopComponent.Registry.PROP_ACTIVATED_NODES"); // NOI18N

                Node[] ns = getActivatedNodes();

                // in reasonable cases activate DataNode
                
                DataObject dobj = (DataObject) getXMLDataObjectLook().getCookie(DataObject.class);
                if ((ns != null) && (ns.length == 0) && (dobj != null) && (dobj.isValid())) {
                    setActivatedNodes (new Node [] { rootNode });
                    explorerPanel.setActivatedNodes (new Node [] { rootNode });
                }

            } else if (DataObject.PROP_VALID.equals (ev.getPropertyName())) {
                if (Boolean.FALSE.equals (ev.getNewValue())) {
                    TreeEditorComponent.this.close();
                }

            } else if (DataObject.PROP_MODIFIED.equals (ev.getPropertyName())) { // document modified
                updateName();
            } else if (Node.PROP_NAME.equals (ev.getPropertyName())) { // name changed
                updateName();
            } else if (Node.PROP_ICON.equals (ev.getPropertyName())) {
                updateIcon();
            }
        }

        public void nodeDestroyed (NodeEvent ev) {
            getXMLDataObjectLook().setModified (false);
            TreeEditorComponent.this.close();
        }

    } // end: class InnerListener


    //
    // class LocalExplorerPanel
    //

    /**
     * This is just "local copy" of ExplorerPanel, because I need to call on it
     * component(De)Activated, which are protected.
     */
    private static class LocalExplorerPanel extends ExplorerPanel {
        /** Serial Version UID */
        private static final long serialVersionUID = 2219619113909147429L;
        
        protected void componentActivated () {
            super.componentActivated();
        }

        protected void componentDeactivated () {
            super.componentDeactivated();
        }
    } // end: class LocalExplorerPanel


    //
    // class TreeEditorNode
    //

    /**
     * Node which is used int TreeEditorComponent and which change default action on not leaf nodes to expand it.
     * {@link http://www.netbeans.org/issues/show_bug.cgi?id=21608}
     */
    private static class TreeEditorNode extends FilterNode {

        public TreeEditorNode (Node original) {
            super (original,
                   original.isLeaf () ? org.openide.nodes.Children.LEAF : new TreeEditorChildren (original)
                   );
        }

        public SystemAction getDefaultAction () {
            if ( isLeaf() ) {
                return super.getDefaultAction();
            } else {
                return null;
            }
        }

    } // end of TreeEditorNode


    //
    // class TreeEditorChildren
    //
    
    /**
     * Children to support {@link TreeEditorNode}.
     */
    private static class TreeEditorChildren extends FilterNode.Children {

        public TreeEditorChildren (Node original) {
            super (original);
        }

        protected Node copyNode (Node node) {
            return new TreeEditorNode (node);
        }

    } // end of TreeEditorChildren

}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.