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-2004 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.modules.properties;

import java.awt.Image;
import java.beans.*;
import java.io.*;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Date;
import java.util.Enumeration;
import java.util.WeakHashMap;
import javax.swing.JEditorPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.EditorKit;
import javax.swing.text.StyledDocument;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoableEdit;

import org.openide.awt.UndoRedo;
import org.openide.cookies.CloseCookie;
import org.openide.cookies.EditCookie;
import org.openide.cookies.EditorCookie;
import org.openide.cookies.PrintCookie;
import org.openide.cookies.SaveCookie;
import org.openide.filesystems.FileChangeAdapter;
import org.openide.filesystems.FileEvent;
import org.openide.filesystems.FileLock;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataObject;
import org.openide.nodes.Node;
import org.openide.text.CloneableEditor;
import org.openide.text.CloneableEditorSupport;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;
import org.openide.util.WeakListener;
import org.openide.util.Utilities;
import org.openide.windows.CloneableOpenSupport;
import org.openide.windows.CloneableTopComponent;
import org.openide.util.Task;
import org.openide.util.TaskListener;

/** 
 * Support for viewing .properties files (EditCookie) by opening them in a text editor.
 *
 * @author Petr Jiricka, Peter Zavadsky 
 * @see org.openide.text.CloneableEditorSupport
 */
public class PropertiesEditorSupport extends CloneableEditorSupport 
implements EditCookie, EditorCookie.Observable, PrintCookie, CloseCookie, Serializable {
    
    /** New lines in this file was delimited by '\n'. */
    private static final byte NEW_LINE_N = 0;
    /** New lines in this file was delimited by '\r'. */
    private static final byte NEW_LINE_R = 1;
    /** New lines in this file was delimited by '\r\n'. */
    private static final byte NEW_LINE_RN = 2;
    
    /** The type of new lines. Default is NEW_LINE_N. */
    private byte newLineType = NEW_LINE_N;
    
    /** Visible view of underlying file entry */
    transient PropertiesFileEntry myEntry;
    
    /** Generated serial version UID. */
    static final long serialVersionUID =1787354011149868490L;
    
    
    /** Constructor. */
    public PropertiesEditorSupport(PropertiesFileEntry entry) {
        super(new Environment(entry),
              org.openide.util.lookup.Lookups.singleton(entry.getDataObject()));
        this.myEntry = entry;
        initialize();
    }
    
    
    /** Sets MIME type for this support. */
    public void initialize() {
        setMIMEType (PropertiesDataObject.MIME_PROPERTIES);
    }
    
    /** 
     * Overrides superclass method.
     * Should test whether all data is saved, and if not, prompt the user
     * to save. Called by my topcomponent when it wants to close its last topcomponent, but the table editor may still be open
     * @return true if everything can be closed
     */
    protected boolean canClose () {
        // if the table is open, can close without worries, don't remove the save cookie
        if (hasOpenedTableComponent()){
            return true;
        }else{
            DataObject propDO = myEntry.getDataObject();
            if (propDO == null || !propDO.isModified()) return true;
            return super.canClose();
        }
    }
    
    /** 
     * Overrides superclass method.
     * @return the {@link CloneableEditor} for this support
     */
    protected CloneableEditor createCloneableEditor() {
        return new PropertiesEditor(this);
    }
    
    /**
     * Overrides superclass method.
     * A method to create a new component. Overridden in subclasses.
     * @return the {@link Editor} for this support
     */
    protected CloneableTopComponent createCloneableTopComponent () {
        // initializes the document if not initialized
        prepareDocument();
        
        CloneableEditor editor = new PropertiesEditor(this);
        return editor;
    }

    /**
     * Overrides superclass method. 
     * Let's the super method create the document and also annotates it
     * with Title and StreamDescription properities.
     * @param kit kit to user to create the document
     * @return the document annotated by the properties
     */
    protected StyledDocument createStyledDocument(EditorKit kit) {
        StyledDocument document = super.createStyledDocument(kit);
        
        // Set additional proerties to document.
        // Set document name property. Used in CloneableEditorSupport.
        document.putProperty(Document.TitleProperty, myEntry.getFile().toString());
        
        // Set dataobject to stream desc property.
        document.putProperty(Document.StreamDescriptionProperty, myEntry.getDataObject());
        
        return document;
    }

    /**
     * Reads the file from the stream, filter the guarded section
     * comments, and mark the sections in the editor. Overrides superclass method. 
     * @param document the document to read into
     * @param inputStream the open stream to read from
     * @param editorKit the associated editor kit
     * @throws IOException if there was a problem reading the file
     * @throws BadLocationException should not normally be thrown
     * @see #saveFromKitToStream
     */
    protected void loadFromStreamToKit(StyledDocument document, InputStream inputStream, EditorKit editorKit)
    throws IOException, BadLocationException {
        NewLineReader newLineReader = new NewLineReader(inputStream);
        
        try {
            editorKit.read(newLineReader, document, 0);
            newLineType = newLineReader.getNewLineType();
        } finally {
            newLineReader.close();
        }
    }

    /** 
     * Adds new lines according actual value of newLineType variable.
     * Overrides superclass method.
     * @param document the document to write from
     * @param editorKit the associated editor kit
     * @param ouputStream the open stream to write to
     * @throws IOException if there was a problem writing the file
     * @throws BadLocationException should not normally be thrown
     * @see #loadFromStreamToKit
     */
    protected void saveFromKitToStream(StyledDocument document, EditorKit editorKit, OutputStream outputStream)
    throws IOException, BadLocationException {
        Writer writer = new NewLineWriter(outputStream, newLineType);
        
        try {
            editorKit.write(writer, document, 0, document.getLength());
        } finally {
            writer.flush();
            writer.close();
        }
    }
    
    /** 
     * Adds a save cookie if the document has been marked modified. Overrides superclass method. 
     * @return true if the environment accepted being marked as modified
     *    or false if it refused it and the document should still be unmodified
     */
    protected boolean notifyModified () {
        // Reparse file.
        myEntry.getHandler().autoParse();
        
        if (super.notifyModified()) {
            ((Environment)env).addSaveCookie();
            
            return true;
        } else {
            return false;
        }
    }
    
    protected Task reloadDocument(){
        Task tsk = super.reloadDocument();
        tsk.addTaskListener(new TaskListener(){
            public void taskFinished(Task task){
                myEntry.getHandler().autoParse();
            }
        });
        return tsk;
    }

    /** Overrides superclass method. Adds checking for opened Table component. */
    protected void notifyUnmodified () {
        super.notifyUnmodified();
        
        ((Environment)env).removeSaveCookie();
    }
    
    /** Overrides superclass method. Adds checking for opened Table panel. */
    protected void notifyClosed() {
        // Close document only in case there is not open table editor.
        if(!hasOpenedTableComponent()) {
            boolean wasModified = isModified();
            super.notifyClosed();
            if (wasModified) {
                // #21850. Don't reparse invalid or virtual file.
                if(myEntry.getFile().isValid() && !myEntry.getFile().isVirtual()) {
                    myEntry.getHandler().reparseNowBlocking();
                }
            }
        }
    }

    /** 
     * Overrides superclass abstract method. 
     * Message to display when an object is being opened.
     * @return the message or null if nothing should be displayed
     */
    protected String messageOpening() {
        String name = myEntry.getDataObject().getPrimaryFile().getName()+"("+Util.getLocaleLabel(myEntry)+")"; // NOI18N
        
        return NbBundle.getMessage(
            PropertiesEditorSupport.class,
            "LBL_ObjectOpen", // NOI18N
            name
        );
    }
    
    /**
     * Overrides superclass abstract method. 
     * Message to display when an object has been opened.
     * @return the message or null if nothing should be displayed
     */
    protected String messageOpened() {
        String name = myEntry.getDataObject().getPrimaryFile().getName()+"("+Util.getLocaleLabel(myEntry)+")"; // NOI18N        
        
        return NbBundle.getMessage(
            PropertiesEditorSupport.class,
            "LBL_ObjectOpened", // NOI18N
            name
       );
    }

    /** 
     * Overrides superclass abstract method. 
     * Constructs message that should be used to name the editor component.
     * @return name of the editor
     */
    protected String messageName () {
        FileObject entry = myEntry.getDataObject().getPrimaryFile();
        String name = entry.getName()+"("+Util.getLocaleLabel(myEntry)+")"; // NOI18N
        int version;
        
        if(isModified()) {
            version = entry.canWrite() ? 1 : 2;
        } else {
            version = entry.canWrite() ? 3 : 0;
        }
        return NbBundle.getMessage (PropertiesEditorSupport.class, "LBL_EditorName", // NOI18N
            new Integer (version), name );
    }
    
    /** 
     * Overrides superclass abstract method.
     * Is modified and is being closed.
     * @return text to show to the user
     */
    protected String messageSave () {
        String name = myEntry.getDataObject().getPrimaryFile().getName()+"("+Util.getLocaleLabel(myEntry)+")"; // NOI18N        
        
        return NbBundle.getMessage (
            PropertiesEditorSupport.class,
            "MSG_SaveFile", // NOI18N
            name
        );
    }
    
    /** 
     * Overrides superclass abstract method.
     * Text to use as tooltip for component.
     * @return text to show to the user
     */
    protected String messageToolTip () {
        // copied from DataEditorSupport, more or less
        FileObject fo = myEntry.getFile();
        return FileUtil.getFileDisplayName(fo);
    }
    
    /** Overrides superclass method. Gets UndoRedo manager which maps 
     * UndoalbleEdit's to StampFlag's. */
    protected UndoRedo.Manager createUndoRedoManager () {
        return new UndoRedoStampFlagManager();
    }
    
    /** 
     * Helper method. Hack on superclass getUndoRedo() method, to widen its protected modifier. 
     * Needs to be accessed from outside this class (in PropertiesOpen). 
     * @see PropertiesOpen 
     */
    UndoRedo.Manager getUndoRedoManager() {
        return super.getUndoRedo();
    }
    
    /** 
     * Helper method. Used only by PropertiesOpen support when closing last Table component.
     * Note: It's quite ugly by-pass of notifyClosed() method. Should be revised. 
     */
    void forceNotifyClosed() {
        super.notifyClosed();
    }
    
    /** Helper method. 
     * @return newLineType.*/
    byte getNewLineType() {
        return newLineType;
    }
    
    /** Helper method. Saves this entry. */
    private void saveThisEntry() throws IOException {
        super.saveDocument();
        // #32777 - it can happen that save operation was interrupted
        // and file is still modified. Mark it unmodified only when it is really
        // not modified.
        if (!env.isModified()) {
            myEntry.setModified(false);
        }
    }
    
    /** Helper method. Sets CloneableTopComponent.Ref for this support. 
     * @see org.openide.windows.CloneableTopComponent.Ref */
    private void setRef(CloneableTopComponent.Ref ref) {
        allEditors = ref;
    }
    
    /** Helper method. 
     * @return whether there is an table view opened */
    public synchronized boolean hasOpenedTableComponent() {
        return ((PropertiesDataObject)myEntry.getDataObject()).getOpenSupport().hasOpenedTableComponent();
    }
    
    /**
     * Helper method.
     * @return whether there is an open editor component. */
    public synchronized boolean hasOpenedEditorComponent() {
        Enumeration en = allEditors.getComponents ();
        return en.hasMoreElements ();
    }

    /** Class which exist only due comaptibility with version 3.0. */    
    private static final class Env extends Environment {
        /** Generated Serialized Version UID. */
        static final long serialVersionUID = -9218186467757330339L;

        /** Used for deserialization. */
        private PropertiesFileEntry entry;

        /** */
        public Env(PropertiesFileEntry entry) {
            super(entry);
        }

        /** Adds passing entry field to superclass. */
        private void readObject(java.io.ObjectInputStream in)
            throws IOException, ClassNotFoundException {
                in.defaultReadObject();
                
                if(this.entry != null)
                    super.entry = this.entry;
        }
    }

    
    /** Nested class. Implementation of ClonableEditorSupport.Env interface. */
    private static class Environment implements CloneableEditorSupport.Env,
    PropertyChangeListener, SaveCookie {
        
        /** generated Serialized Version UID */
        static final long serialVersionUID = 354528097109874355L;
            
        /** Entry on which is support build. */
        protected PropertiesFileEntry entry;
            
        /** Lock acquired after the first modification and used in save method. */
        private transient FileLock fileLock;
            
        /** Spport for firing of property changes. */
        private transient PropertyChangeSupport propSupp;
        
        /** Support for firing of vetoable changes. */
        private transient VetoableChangeSupport vetoSupp;
            
            
        /** Constructor.
         * @param obj this support should be associated with
         */
        public Environment (PropertiesFileEntry entry) {
            this.entry = entry;
            entry.getFile().addFileChangeListener(new EnvironmentListener(this));
            entry.addPropertyChangeListener(this);
        }

        /** Implements CloneableEditorSupport.Env inetrface. Adds property listener. */
        public void addPropertyChangeListener(PropertyChangeListener l) {
            prop().addPropertyChangeListener (l);
        }

        
        /** Accepts property changes from entry and fires them to own listeners. */
        public void propertyChange(PropertyChangeEvent evt) {
            // We will handle the object invalidation here.
            if(DataObject.PROP_VALID.equals(evt.getPropertyName ())) { 
                // do not check it if old value is not true
                if(Boolean.FALSE.equals(evt.getOldValue())) return;

                // loosing validity
                PropertiesEditorSupport support = (PropertiesEditorSupport)findCloneableOpenSupport();
                if(support != null) {
                    
                    // mark the object as not being modified, so nobody
                    // will ask for save
                    unmarkModified();

                    support.close(false);
                }
            } else {
                firePropertyChange (
                    evt.getPropertyName(),
                    evt.getOldValue(),
                    evt.getNewValue()
                );
            }
        }
        
        /** Implements CloneableEditorSupport.Env inetrface. Removes property listener. */
        public void removePropertyChangeListener(PropertyChangeListener l) {
            prop().removePropertyChangeListener (l);
        }
            
        /** Implements CloneableEditorSupport.Env inetrface. Adds veto listener. */
        public void addVetoableChangeListener(VetoableChangeListener l) {
            veto().addVetoableChangeListener (l);
        }
            
        /** Implements CloneableEditorSupport.Env inetrface. Removes veto listener. */
        public void removeVetoableChangeListener(VetoableChangeListener l) {
            veto().removeVetoableChangeListener (l);
        }

        /** Overrides superclass method.
         * Note: in fact it returns CloneableEditorSupport instance.
         * @return the support or null if the environemnt is not in valid
         * state and the CloneableOpenSupport cannot be found for associated
         * entry object
         */
        public CloneableOpenSupport findCloneableOpenSupport() {
            return (PropertiesEditorSupport)entry.getCookieSet().getCookie(EditCookie.class);
        }

        /**
         * Implements CloneableEditorSupport.Env interface.
         * 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() {
            return entry.getDataObject().isValid();
        }
            
        /**
         * Implements CloneableEditorSupport.Env interface.
         * Test whether the object is modified or not.
         * @return true if the object is modified
         */
        public boolean isModified() {
            return entry.isModified();
        }

        /**
         * Implements CloneableEditorSupport.Env interface.
         * First of all tries to lock the primary file and
         * if it succeeds it marks the data object 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 {
            if (fileLock == null || !fileLock.isValid()) {
                fileLock = entry.takeLock();
            }
            
            entry.setModified(true);
        }
            
        /**
         * Implements CloneableEditorSupport.Env interface.
         * Reverse method that can be called to make the environment
         * unmodified.
         */
        public void unmarkModified() {
            if (fileLock != null && fileLock.isValid()) {
                fileLock.releaseLock();
            }
            
            entry.setModified(false);
        }

        /**
         * Implements CloneableEditorSupport.Env interface.
         * Mime type of the document.
         * @return the mime type to use for the document
         */
        public String getMimeType() {
            return entry.getFile().getMIMEType();
        }
            
        /**
         * Implements CloneableEditorSupport.Env interface.
         * The time when the data has been modified. */
        public Date getTime() {
            // #32777 - refresh file object and return always the actual time
            entry.getFile().refresh(false);
            return entry.getFile().lastModified();
        }
            
        /**
         * Implements CloneableEditorSupport.Env interface.
         * Obtains the input stream.
         * @exception IOException if an I/O error occures
         */
        public InputStream inputStream() throws IOException {
            return entry.getFile().getInputStream();
        }
            
        /**
         * Implements CloneableEditorSupport.Env interface.
         * Obtains the output stream.
         * @exception IOException if an I/O error occures
         */
        public OutputStream outputStream() throws IOException {
            return entry.getFile().getOutputStream(fileLock);
        }

        /**
         * Implements SaveCookie interface. 
         * Invoke the save operation.
         * @throws IOException if the object could not be saved
         */
        public void save() throws IOException {
            // Do saving job. Note it gets editor support, not open support.
            ((PropertiesEditorSupport)findCloneableOpenSupport()).saveThisEntry();
        }
            
        /** Fires property change.
         * @param name the name of property that changed
         * @param oldValue old value
         * @param newValue new value
         */
        private void firePropertyChange (String name, Object oldValue, Object newValue) {
            prop().firePropertyChange (name, oldValue, newValue);
        }
            
        /** Fires vetoable change.
         * @param name the name of property that changed
         * @param oldValue old value
         * @param newValue new value
         */
        private void fireVetoableChange (String name, Object oldValue, Object newValue) throws PropertyVetoException {
                veto ().fireVetoableChange (name, oldValue, newValue);
        }
            
        /** Lazy getter for property change support. */
        private PropertyChangeSupport prop() {
            if (propSupp == null) {
                synchronized (this) {
                    if (propSupp == null) {
                        propSupp = new PropertyChangeSupport (this);
                    }
                }
            }
            
            return propSupp;
        }
            
        /** Lazy getter for vetoable support. */
        private VetoableChangeSupport veto() {
            if (vetoSupp == null) {
                synchronized (this) {
                    if (vetoSupp == null) {
                        vetoSupp = new VetoableChangeSupport (this);
                    }
                }
            }
            return vetoSupp;
        }
            
        /** Helper method. Adds save cookie to the entry. */
        private void addSaveCookie() {
            if (entry.getCookie(SaveCookie.class) == null) {
                entry.getCookieSet().add(this);
            }
            ((PropertiesDataObject)entry.getDataObject()).updateModificationStatus();
        }
            
        /** Helper method. Removes save cookie from the entry. */
        private void removeSaveCookie() {
            // remove Save cookie from the entry
            SaveCookie sc = (SaveCookie)entry.getCookie(SaveCookie.class);
            
            if (sc != null && sc.equals(this)) {
                entry.getCookieSet().remove(this);
            }
            
            RequestProcessor.postRequest(new Runnable() {
                public void run() {
                    ((PropertiesDataObject)entry.getDataObject()).updateModificationStatus();
                }
            });
        }
            
        /** Called from the EnvironmnetListener
         * @param expected is the change expected
         * @param time of the change
         */
        private void fileChanged(boolean expected, long time) {
            if (expected) {
                // newValue = null means do not ask user whether to reload
                firePropertyChange (PROP_TIME, null, null);
            } else {
                firePropertyChange (PROP_TIME, null, new Date (time));
            }
        }

        
        /** Called from the EnvironmentListener.
         * The components are going to be closed anyway and in case of
         * modified document its asked before if to save the change. */
        private void fileRemoved() {
            try {
                fireVetoableChange(PROP_VALID, Boolean.TRUE, Boolean.FALSE);
            } catch(PropertyVetoException pve) {
                // Ignore it and close anyway. File doesn't exist anymore.
            }
            
            firePropertyChange(PROP_VALID, Boolean.TRUE, Boolean.FALSE);
        }
    } // End of nested class Environment.

    
    /** Weak listener on file object that notifies the Environment object
     * that a file has been modified. */
    private static final class EnvironmentListener extends FileChangeAdapter {
        
        /** Reference of Environment */
        private Reference reference;
        
        /** @param environment Environment to use
         */
        public EnvironmentListener(Environment environment) {
            reference = new WeakReference(environment);
        }
        
        /** Fired when a file is changed.
         * @param fe the event describing context where action has taken place
         */
        public void fileChanged(FileEvent evt) {
            Environment environment = (Environment)reference.get();
            if (environment != null) {
                if(!environment.entry.getFile().equals(evt.getFile()) ) {
                    // If the FileObject was changed.
                    // Remove old listener from old FileObject.
                    evt.getFile().removeFileChangeListener(this);
                    // Add new listener to new FileObject.
                    environment.entry.getFile().addFileChangeListener(new EnvironmentListener(environment));
                    return;
                }

                // #16403. See DataEditorSupport.EnvListener.
                if(evt.getFile().isVirtual()) {
                    environment.entry.getFile().removeFileChangeListener(this);
                    // File doesn't exist on disk -> simulate env is invalid,
                    // even the fileObject could be valid, see VCS FS.
                    environment.fileRemoved();
                    environment.entry.getFile().addFileChangeListener(this);
                } else {
                    environment.fileChanged(evt.isExpected(), evt.getTime());
                }
            }
        }
    } // End of nested class EnvironmentListener.

    
    /** Inner class for opening editor view at a given key. */
    public class PropertiesEditAt implements EditCookie {

        /** Key at which should be pane opened. (Cursor will be at the position of that key). */
        private String key;
        
        
        /** Constructor. */
        PropertiesEditAt(String key) {
            this.key   = key;
        }
        
        
        /** Setter for key. */
        public void setKey(String key) {
            this.key = key;
        }
        
        /** Implementation of EditCookie interface. */
        public void edit() {
            PropertiesEditor editor = (PropertiesEditor)PropertiesEditorSupport.super.openCloneableTopComponent();
            editor.requestActive();
            
            Element.ItemElem item = myEntry.getHandler().getStructure().getItem(key);
            if (item != null) {
                int offset = item.getKeyElem().getBounds().getBegin().getOffset();
                if (editor.getPane() != null && editor.getPane().getCaret() !=null)
                    editor.getPane().getCaret().setDot(offset);
            }
        }
    } // End of inner class PropertiesEditAt.

    
    /** Cloneable top component to hold the editor kit. */
    public static class PropertiesEditor extends CloneableEditor {
        
        /** Holds the file being edited. */
        protected transient PropertiesFileEntry entry;
        
        /** Listener for entry's save cookie changes. */
        private transient PropertyChangeListener saveCookieLNode;
        
        /** Generated serial version UID. */
        static final long serialVersionUID =-2702087884943509637L;
        
        
        /** Constructor for deserialization */
        public PropertiesEditor() {
            super();
        }
        
        /** Creates new editor */
        public PropertiesEditor(PropertiesEditorSupport support) {
            super(support);
            initialize();
        }

        
        /** Initializes object, used in construction and deserialization. */
        private void initialize() {
            this.entry = ((PropertiesEditorSupport)cloneableEditorSupport()).myEntry;
            
            // add to CloneableEditorSupport - patch for a bug in deserialization
            ((PropertiesEditorSupport)cloneableEditorSupport()).setRef(getReference());
            
            Node n = entry.getNodeDelegate ();
            setActivatedNodes (new Node[] { n });
            
            updateName();
            
            // entry to the set of listeners
            saveCookieLNode = new PropertyChangeListener() {
                public void propertyChange(PropertyChangeEvent evt) {
                    if (Node.PROP_COOKIE.equals(evt.getPropertyName()) ||
                    DataObject.PROP_NAME.equals(evt.getPropertyName())) {
                        PropertiesEditor.super.updateName();
                    }
                }
            };
            this.entry.addPropertyChangeListener(
            WeakListener.propertyChange(saveCookieLNode, this.entry));
        }

        /**
         * Overrides superclass method. 
         * When closing last view, also close the document.
         * @return true if close succeeded
         */
        protected boolean closeLast () {
            return super.closeLast();
        }

        /** Overrides superclass method. Gets Icon. */
        public Image getIcon () {
            return Utilities.loadImage("org/netbeans/modules/properties/propertiesLocale.gif"); // NOI18N
        }
        
        /** Overrides superclass method. Gets help context. */
        public HelpCtx getHelpCtx() {
            return new HelpCtx(Util.HELP_ID_EDITLOCALE);
        }
        
        /**
         * Overrides superclass method.
         * Serialize this top component.
         * @param out the stream to serialize to
         */
        public void writeExternal (ObjectOutput out) throws IOException {
            super.writeExternal(out);
        }
        
        /**
         * Overrides superclass method. 
         * Deserialize this top component.
         * @param in the stream to deserialize from
         */
        public void readExternal (ObjectInput in) throws IOException, ClassNotFoundException {
            super.readExternal(in);
            initialize();
        }
        
        /** Getter for pane. */
        private JEditorPane getPane() {
            return pane;
        }
    } // End of nested class PropertiesEditor.
    

    /** This stream is able to filter various new line delimiters and replace them by \n. */
    static class NewLineReader extends BufferedReader {
        
        /** The count of types new line delimiters used in the file */
        int[] newLineTypes;
        
        
        /** Creates new stream.
         * @param is encapsulated input stream.
         * @param justFilter The flag determining if this stream should
         *        store the guarded block information. True means just filter,
         *        false means store the information.
         */
        public NewLineReader(InputStream is) throws IOException {
            super(new InputStreamReader(is, "8859_1")); // NOI18N
            
            newLineTypes = new int[] { 0, 0, 0 };
        }

        
        /** Overrides superclass method. Reads one character.
         * @return next char or -1 if the end of file was reached.
         * @exception IOException if any problem occured.
         */
        public int read() throws IOException {
            int nextToRead = super.read();
            
            if (nextToRead == -1)
                return -1;
            
            if (nextToRead == '\r') {
                nextToRead = super.read();
                
                while (nextToRead == '\r')
                    nextToRead = super.read();
                if (nextToRead == '\n') {
                    nextToRead = super.read();
                    newLineTypes[NEW_LINE_RN]++;
                    return '\n';
                } else {
                    newLineTypes[NEW_LINE_R]++;
                    return '\n';
                }
            }
            if (nextToRead == '\n') {
                nextToRead = super.read();
                newLineTypes[NEW_LINE_N]++;
                return '\n';
            }
            
            return nextToRead;
        }

        /** Gets new line type. */
        public byte getNewLineType() {
            if (newLineTypes[0] > newLineTypes[1]) {
                return (newLineTypes[0] > newLineTypes[2]) ? NEW_LINE_N : NEW_LINE_RN;
            } else {
                return (newLineTypes[1] > newLineTypes[2]) ? NEW_LINE_R : NEW_LINE_RN;
            }
        }
        
    } // End of nested class NewLineReader.
    
    
    /** This stream is used for changing the new line delimiters.
     * Replaces the '\n' by '\n', '\r' or "\r\n". */
    static class NewLineWriter extends BufferedWriter {
        
        /** The type of new line delimiter */
        byte newLineType;

        
        /** Creates new stream.
         * @param stream Underlaying stream
         * @param newLineType The type of new line delimiter
         */
        public NewLineWriter(OutputStream stream, byte newLineType) throws UnsupportedEncodingException {
            super(new OutputStreamWriter(stream, "8859_1"));
            
            this.newLineType = newLineType;
        }
        
        
        /** Write one character. New line char replaces according the newLineType character.
         * @param character character to write.
         */
        public void write(int character) throws IOException {
            if(character == '\r')
                // Do nothing.
                return;
            if(character == '\n') {
                if(newLineType == NEW_LINE_R) {
                    // Replace new line by \r.
                    super.write('\r');
                } else if(newLineType == NEW_LINE_N) {
                    // Replace new line by \n.
                    super.write('\n');
                } else if(newLineType == NEW_LINE_RN) {
                    // Replace new line by \r\n.
                    super.write('\r');
                    super.write('\n');
                }
            } else {
                super.write(character);
            }
        }
        
    } // End of nested class NewLineWriter.
    
    
    /** Inner class. UndoRedo manager which saves a StampFlag
     * for each UndoAbleEdit.
     */
    class UndoRedoStampFlagManager extends UndoRedo.Manager {
        
        /** Hash map of weak reference keys (UndoableEdit's) to their StampFlag's. */
        WeakHashMap stampFlags =  new WeakHashMap(5);
        
        /** Overrides superclass method. Adds StampFlag to UndoableEdit. */
        public synchronized boolean addEdit(UndoableEdit anEdit) {
            stampFlags.put(anEdit, new StampFlag(System.currentTimeMillis(),
                ((PropertiesDataObject)PropertiesEditorSupport.this.myEntry.getDataObject()).getOpenSupport().atomicUndoRedoFlag ));
            return super.addEdit(anEdit);
        }
        
        /** Overrides superclass method. Adds StampFlag to UndoableEdit. */
        public boolean replaceEdit(UndoableEdit anEdit) {
            stampFlags.put(anEdit, new StampFlag(System.currentTimeMillis(), 
                ((PropertiesDataObject)PropertiesEditorSupport.this.myEntry.getDataObject()).getOpenSupport().atomicUndoRedoFlag ));
            return super.replaceEdit(anEdit);
        }
        
        /** Overrides superclass method. Updates time stamp for the edit. */
        public synchronized void undo() throws CannotUndoException {
            UndoableEdit anEdit = editToBeUndone();
            if(anEdit != null) {
                Object atomicFlag = ((StampFlag)stampFlags.get(anEdit)).getAtomicFlag(); // atomic flag remains
                super.undo();
                stampFlags.put(anEdit, new StampFlag(System.currentTimeMillis(), atomicFlag));
            }
        }
        
        /** Overrides superclass method. Updates time stamp for that edit. */
        public synchronized void redo() throws CannotRedoException {
            UndoableEdit anEdit = editToBeRedone();
            if(anEdit != null) {
                Object atomicFlag = ((StampFlag)stampFlags.get(anEdit)).getAtomicFlag(); // atomic flag remains
                super.redo();
                stampFlags.put(anEdit, new StampFlag(System.currentTimeMillis(), atomicFlag));
            }
        }
        
        /** Method which gets time stamp of next Undoable edit to be undone. 
         * @ return time stamp in milliseconds or 0 (if don't exit edit to be undone). */
        public long getTimeStampOfEditToBeUndone() {
            UndoableEdit nextUndo = editToBeUndone();
            if(nextUndo == null)
                return 0L;
            else
                return ((StampFlag)stampFlags.get(nextUndo)).getTimeStamp();
        }
        
        /** Method which gets time stamp of next Undoable edit to be redone.
         * @ return time stamp in milliseconds or 0 (if don't exit edit to be redone). */
        public long getTimeStampOfEditToBeRedone() {
            UndoableEdit nextRedo = editToBeRedone();
            if(nextRedo == null)
                return 0L;
            else
                return ((StampFlag)stampFlags.get(nextRedo)).getTimeStamp();
        }
        
        /** Method which gets atomic flag of next Undoable edit to be undone. 
         * @ return atomic flag in milliseconds or 0 (if don't exit edit to be undone). */
        public Object getAtomicFlagOfEditToBeUndone() {
            UndoableEdit nextUndo = editToBeUndone();
            if(nextUndo == null)
                return null;
            else
                return ((StampFlag)stampFlags.get(nextUndo)).getAtomicFlag();
        }
        
        /** Method which gets atomic flag of next Undoable edit to be redone.
         * @ return time stamp in milliseconds or 0 (if don't exit edit to be redone). */
        public Object getAtomicFlagOfEditToBeRedone() {
            UndoableEdit nextRedo = editToBeRedone();
            if(nextRedo == null)
                return null;
            else
                return ((StampFlag)stampFlags.get(nextRedo)).getAtomicFlag();
        }
        
    } // End of inner class UndoRedoTimeStampManager.

    /** Simple nested class for storing time stamp and atomic flag used 
     * in UndoRedoStampFlagManager.
     */
    static class StampFlag {
        
        /** Time stamp when was an UndoableEdit (to which is this class mapped via 
         * UndoRedoStampFlagManager,) was created, replaced, undone, or redone. */
        private long timeStamp;
        
        /** Atomic flag. If this object is not null it means that an UndoableEdit ( to which
         * is this class mapped via UndoRedoStampFlagManager,) was created as part of one 
         * action which could consist from more UndoableEdits in differrent editor supports.
         * These Undoable edits are marked with this (i.e. same) object. */
        private Object atomicFlag;
        
        /** Consructor. */
        public StampFlag(long timeStamp, Object atomicFlag) {
            this.timeStamp = timeStamp;
            this.atomicFlag = atomicFlag;            
        }
        
        /** Getter for time stamp. */
        public long getTimeStamp() {
            return timeStamp;
        }
        
        /** Setter for time stamp. */
        public void setTimeStamp(long timeStamp) {
            this.timeStamp = timeStamp;
        }
        
        /** Getter for atomic flag.
         @ return Returns null if is not linked with more Undoable edits.*/
        public Object getAtomicFlag() {
            return atomicFlag;
        }
    } // End of nested class TimeStamp.
}
... 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.