CloneableOpenSupport.java :  » IDE-Netbeans » openide » org » openide » windows » Java Open Source

Java Open Source » IDE Netbeans » openide 
openide » org » openide » windows » CloneableOpenSupport.java
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common
 * Development and Distribution License("CDDL") (collectively, the
 * "License"). You may not use this file except in compliance with the
 * License. You can obtain a copy of the License at
 * http://www.netbeans.org/cddl-gplv2.html
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 * specific language governing permissions and limitations under the
 * License.  When distributing the software, include this License Header
 * Notice in each file and include the License file at
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the GPL Version 2 section of the License file that
 * accompanied this code. If applicable, add the following below the
 * License Header, with the fields enclosed by brackets [] replaced by
 * your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * Contributor(s):
 *
 * The Original Software is NetBeans. The Initial Developer of the Original
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
 * Microsystems, Inc. All Rights Reserved.
 *
 * If you wish your version of this file to be governed by only the CDDL
 * or only the GPL Version 2, indicate your decision by adding
 * "[Contributor] elects to include this software in this distribution
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 * single choice of license, a recipient has the option to distribute
 * your version of this file under either the CDDL, the GPL Version 2 or
 * to extend the choice of license to its licensees as provided above.
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 * Version 2 license, then the option applies only if the new code is
 * made subject to such option by the copyright holder.
 */
package org.openide.windows;

import org.openide.awt.StatusDisplayer;
import org.openide.util.*;

import java.beans.*;


/** Simple support for an openable objects.
* Can be used either as an {@link org.openide.cookies.OpenCookie},
* {@link org.openide.cookies.ViewCookie}, or {@link org.openide.cookies.CloseCookie},
* depending on which cookies the subclass implements.
*
* @author Jaroslav Tulach
*/
public abstract class CloneableOpenSupport extends Object {
    private static java.awt.Container container;

    /** the environment that provides connection to outside world */
    protected Env env;

    /** All opened editors on this file.
     * <em>Warning:</em> Treat this field like <code>final</code>.
     * Internally the instance is used as <code>WeakListener</code>
     * on <code>Env</code> validity changes.
     * Changing the instance in subclasses would lead to breaking
     * of that listening, thus to errorneous behaviour. */
    protected CloneableTopComponent.Ref allEditors;

    /** New support for a given environment.
    * @param env environment to take all date from/to
    */
    public CloneableOpenSupport(Env env) {
        this.env = env;

        Listener l = new Listener(env);
        this.allEditors = l;

        // attach property change listener to be informed about loosing validity
        env.addPropertyChangeListener(org.openide.util.WeakListeners.propertyChange(l, env));

        // attach vetoable change listener to be cancel loosing validity when modified
        env.addVetoableChangeListener(org.openide.util.WeakListeners.vetoableChange(l, env));
    }

    /** Opens and focuses or just focuses already opened
     * <code>CloneableTopComponent</code>.
     * <p><b>Note: The actual processing of this method is scheduled into AWT thread
     * in case it is called from other than the AWT thread.</b></p>
     * @see org.openide.cookies.OpenCookie#open
     * @see #openCloneableTopComponent
     */
    public void open() {
        //Bugfix #10688 open() is now run in AWT thread
        Mutex.EVENT.writeAccess(
            new Runnable() {
                public void run() {
                    CloneableTopComponent editor = openCloneableTopComponent();
                    editor.requestActive();
                }
            }
        );
    }

    /** Focuses existing component to view, or if none exists creates new.
    * The default implementation simply calls {@link #open}.
    * @see org.openide.cookies.ViewCookie#view
    */
    public void view() {
        open();
    }

    /** Focuses existing component to view, or if none exists creates new.
    * The default implementation simply calls {@link #open}.
    * @see org.openide.cookies.EditCookie#edit
    */
    public void edit() {
        open();
    }

    /** Closes all components.
    * @return <code>true</code> if every component is successfully closed or <code>false</code> if the user cancelled the request
    * @see org.openide.cookies.CloseCookie#close
    */
    public boolean close() {
        return close(true);
    }

    /** Closes all opened windows.
    * @param ask true if we should ask user
    * @return true if sucesfully closed
    */
    protected boolean close(final boolean ask) {
        if (allEditors.isEmpty()) {
            return true;
        }

        //Bugfix #10688 close() is now run in AWT thread
        //also bugfix of 10714 - whole close (boolean) is run in AWT thread
        Boolean ret = Mutex.EVENT.writeAccess(
                new Mutex.Action<Boolean>() {
                    public Boolean run() {
                        //synchronized (allEditors) {
                        synchronized (getLock()) {
                            // user canceled the action
                            if (ask && !canClose()) {
                                return Boolean.FALSE;
                            }

                            java.util.Enumeration en = allEditors.getComponents();

                            while (en.hasMoreElements()) {
                                TopComponent c = (TopComponent) en.nextElement();

                                if (!c.close()) {
                                    return Boolean.FALSE;
                                }
                            }
                        }

                        return Boolean.TRUE;
                    }
                }
            );

        return ret.booleanValue();
    }

    /** Should test whether all data is saved, and if not, prompt the user
    * to save.
    * The default implementation returns <code>true</code>.
    *
    * @return <code>true</code> if everything can be closed
    */
    protected boolean canClose() {
        return true;
    }

    /** Simply open for an editor. */
    protected final CloneableTopComponent openCloneableTopComponent() {
        //synchronized (allEditors) {
        synchronized (getLock()) {
            CloneableTopComponent ret = allEditors.getArbitraryComponent();

            if (ret != null) {
                ret.open();

                return ret;
            } else {
                // no opened editor
                String msg = messageOpening();

                if (msg != null) {
                    StatusDisplayer.getDefault().setStatusText(msg);
                }

                CloneableTopComponent editor = createCloneableTopComponent();
                editor.setReference(allEditors);
                editor.open();

                msg = messageOpened();

                if (msg == null) {
                    msg = ""; // NOI18N
                }

                StatusDisplayer.getDefault().setStatusText(msg);

                return editor;
            }
        }
    }

    /** Creates lock object used in close and openCloneableTopComponent. */
    private Object getLock() {
        if (container == null) {
            container = new java.awt.Container();
        }

        return container.getTreeLock();
    }

    /** A method to create a new component. Must be overridden in subclasses.
    * @return the cloneable top component for this support
    */
    protected abstract CloneableTopComponent createCloneableTopComponent();

    /** Message to display when an object is being opened.
    * @return the message or null if nothing should be displayed
    */
    protected abstract String messageOpening();

    /** Message to display when an object has been opened.
    * @return the message or null if nothing should be displayed
    */
    protected abstract String messageOpened();

    /** Abstract interface that is used by CloneableOpenSupport to
    * talk to outside world.
    */
    public static interface Env extends java.io.Serializable {
        /** that is fired when the objects wants to mark itself as
        * invalid, so all components should be closed.
        */
        public static final String PROP_VALID = "valid"; // NOI18N

        /** that is fired when the objects wants to mark itself modified
        * or not modified.
        */
        public static final String PROP_MODIFIED = "modified"; // NOI18N

        /** Adds property listener.
        */
        public void addPropertyChangeListener(PropertyChangeListener l);

        /** Removes property listener.
        */
        public void removePropertyChangeListener(PropertyChangeListener l);

        /** Adds veto listener.
        */
        public void addVetoableChangeListener(VetoableChangeListener l);

        /** Removes veto listener.
        */
        public void removeVetoableChangeListener(VetoableChangeListener l);

        /** Test whether the support is in valid state or not.
        * It could be invalid after deserialization when the object it
        * referenced to does not exist anymore.
        *
        * @return true or false depending on its state
        */
        public boolean isValid();

        /** Test whether the object is modified or not.
        * @return true if the object is modified
        */
        public boolean isModified();

        /** Support for marking the environement modified.
        * @exception IOException if the environment cannot be marked modified
        *    (for example when the file is readonly), when such exception
        *    is the support should discard all previous changes
        */
        public void markModified() throws java.io.IOException;

        /** Reverse method that can be called to make the environment
        * unmodified.
        */
        public void unmarkModified();

        /** Method that allows environment to find its
        * cloneable open support.
        */
        public CloneableOpenSupport findCloneableOpenSupport();
    }

    /** Property change & veto listener. To react to dispose/delete of
    * the data object.
    */
    private static final class Listener extends CloneableTopComponent.Ref implements PropertyChangeListener,
        VetoableChangeListener, Runnable {
        /** generated Serialized Version UID */
        static final long serialVersionUID = -1934890789745432531L;

        /** environment to use as connection to outside world */
        private Env env;

        /** Constructor.
        */
        public Listener(Env env) {
            this.env = env;
        }

        /** Getter for the associated CloneableOpenSupport
        * @return the support or null if none was found
        */
        private CloneableOpenSupport support() {
            return env.findCloneableOpenSupport();
        }

        public void propertyChange(PropertyChangeEvent ev) {
            if (Env.PROP_VALID.equals(ev.getPropertyName())) {
                // do not check it if old value is not true
                if (Boolean.FALSE.equals(ev.getOldValue())) {
                    return;
                }

                Mutex.EVENT.readAccess(this);
            }
        }

        /** Closes the support in AWT thread.
         */
        public void run() {
            // loosing validity
            CloneableOpenSupport os = support();

            if (os != null) {
                // mark the object as not being modified, so nobody
                // will ask for save
                env.unmarkModified();

                os.close(false);
            }
        }

        /** Forbids setValid (false) on data object when there is an
        * opened editor.
        *
        * @param ev PropertyChangeEvent
        */
        public void vetoableChange(PropertyChangeEvent ev)
        throws PropertyVetoException {
            if (Env.PROP_VALID.equals(ev.getPropertyName())) {
                // do not check it if old value is not true
                if (Boolean.FALSE.equals(ev.getOldValue())) {
                    return;
                }

                if (env.isModified()) {
                    // if the object is modified 
                    CloneableOpenSupport os = support();

                    if ((os != null) && !os.canClose()) {
                        // is modified and has not been sucessfully closed
                        throw new PropertyVetoException(
                        // [PENDING] this is not a very good detail message!
                        "", ev // NOI18N
                        );
                    }
                }
            }
        }

        /** Resolvable to connect to the right data object. This
        * method is used for connectiong CloneableTopComponents via
        * their CloneableTopComponent.Ref
        */
        public Object readResolve() {
            CloneableOpenSupport os = support();

            if (os == null) {
                // problem! no replace!?
                return this;
            }

            // use the editor support's CloneableTopComponent.Ref
            return os.allEditors;
        }
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.