Logo Search packages:      
Sourcecode: jedit version File versions  Download package

Buffer.java

/*
 * Buffer.java - jEdit buffer
 * :tabSize=8:indentSize=8:noTabs=false:
 * :folding=explicit:collapseFolds=1:
 *
 * Copyright (C) 1998, 2005 Slava Pestov
 * Portions copyright (C) 1999, 2000 mike dillon
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package org.gjt.sp.jedit;

//{{{ Imports
import org.gjt.sp.jedit.browser.VFSBrowser;
import org.gjt.sp.jedit.buffer.*;
import org.gjt.sp.jedit.bufferio.BufferAutosaveRequest;
import org.gjt.sp.jedit.bufferio.BufferIORequest;
import org.gjt.sp.jedit.bufferio.MarkersSaveRequest;
import org.gjt.sp.jedit.bufferset.BufferSet;
import org.gjt.sp.jedit.gui.StyleEditor;
import org.gjt.sp.jedit.io.FileVFS;
import org.gjt.sp.jedit.io.VFS;
import org.gjt.sp.jedit.io.VFSFile;
import org.gjt.sp.jedit.io.VFSManager;
import org.gjt.sp.jedit.msg.BufferUpdate;
import org.gjt.sp.jedit.syntax.*;
import org.gjt.sp.jedit.textarea.JEditTextArea;
import org.gjt.sp.util.IntegerArray;
import org.gjt.sp.util.Log;
import org.gjt.sp.jedit.visitors.JEditVisitorAdapter;
import org.gjt.sp.jedit.visitors.SaveCaretInfoVisitor;
import org.gjt.sp.jedit.options.GeneralOptionPane;

import javax.swing.*;
import javax.swing.text.AttributeSet;
import javax.swing.text.Segment;
import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Vector;
import java.util.Map;
//}}}

/**
 * A <code>Buffer</code> represents the contents of an open text
 * file as it is maintained in the computer's memory (as opposed to
 * how it may be stored on a disk).<p>
 *
 * In a BeanShell script, you can obtain the current buffer instance from the
 * <code>buffer</code> variable.<p>
 *
 * This class does not have a public constructor.
 * Buffers can be opened and closed using methods in the <code>jEdit</code>
 * class.<p>
 *
 * This class is partially thread-safe, however you must pay attention to two
 * very important guidelines:
 * <ul>
 * <li>Changes to a buffer can only be made from the AWT thread.
 * <li>When accessing the buffer from another thread, you must
 * grab a read lock if you plan on performing more than one call, to ensure that
 * the buffer contents are not changed by the AWT thread for the duration of the
 * lock. Only methods whose descriptions specify thread safety can be invoked
 * from other threads.
 * </ul>
 *
 * @author Slava Pestov
 * @version $Id: Buffer.java 16444 2009-11-05 12:50:10Z shlomy $
 */
00089 public class Buffer extends JEditBuffer
{
      //{{{ Some constants
      /**
       * Backed up property.
       * @since jEdit 3.2pre2
       */
00096       public static final String BACKED_UP = "Buffer__backedUp";

      /**
       * Caret info properties.
       * @since jEdit 3.2pre1
       */
00102       public static final String CARET = "Buffer__caret";
      public static final String CARET_POSITIONED = "Buffer__caretPositioned";

      /**
       * Stores a List of {@link org.gjt.sp.jedit.textarea.Selection} instances.
       */
00108       public static final String SELECTION = "Buffer__selection";

      /**
       * This should be a physical line number, so that the scroll
       * position is preserved correctly across reloads (which will
       * affect virtual line numbers, due to fold being reset)
       */
00115       public static final String SCROLL_VERT = "Buffer__scrollVert";
      public static final String SCROLL_HORIZ = "Buffer__scrollHoriz";

      /**
       * Should jEdit try to set the encoding based on a UTF8, UTF16 or
       * XML signature at the beginning of the file?
       */
00122       public static final String ENCODING_AUTODETECT = "encodingAutodetect";

      /**
       * This property is set to 'true' if the file has a trailing newline.
       * @since jEdit 4.0pre1
       */
00128       public static final String TRAILING_EOL = "trailingEOL";

      /**
       * This property is set to 'true' if the file should be GZipped.
       * @since jEdit 4.0pre4
       */
00134       public static final String GZIPPED = "gzipped";
      //}}}

      //{{{ Input/output methods

      //{{{ reload() method
      /**
       * Reloads the buffer from disk, asking for confirmation if the buffer
       * has unsaved changes.
       * @param view The view
       * @since jEdit 2.7pre2
       */
00146       public void reload(View view)
      {
            if (getFlag(UNTITLED))
                  return;
            if(isDirty())
            {
                  String[] args = { path };
                  int result = GUIUtilities.confirm(view,"changedreload",
                        args,JOptionPane.YES_NO_OPTION,
                        JOptionPane.WARNING_MESSAGE);
                  if(result != JOptionPane.YES_OPTION)
                        return;
            }
            view.visit(new SaveCaretInfoVisitor());
            load(view,true);
      } //}}}

      //{{{ load() method
      /**
       * Loads the buffer from disk.
       * @param view The view
       * @param reload If true, user will not be asked to recover autosave
       * file, if any
       *
       * @since 2.5pre1
       */
00172       public boolean load(final View view, final boolean reload)
      {
            if(isPerformingIO())
            {
                  GUIUtilities.error(view,"buffer-multiple-io",null);
                  return false;
            }

            setBooleanProperty(BufferIORequest.ERROR_OCCURRED,false);

            setLoading(true);

            // view text areas temporarily blank out while a buffer is
            // being loaded, to indicate to the user that there is no
            // data available yet.
            if(!getFlag(TEMPORARY))
                  EditBus.send(new BufferUpdate(this,view,BufferUpdate.LOAD_STARTED));

            final boolean loadAutosave;

            if(reload || !getFlag(NEW_FILE))
            {
                  if(file != null)
                        modTime = file.lastModified();

                  // Only on initial load
                  if(!reload && autosaveFile != null && autosaveFile.exists())
                        loadAutosave = recoverAutosave(view);
                  else
                  {
                        if(autosaveFile != null)
                              autosaveFile.delete();
                        loadAutosave = false;
                  }

                  if(!loadAutosave)
                  {
                        VFS vfs = VFSManager.getVFSForPath(path);

                        if(!checkFileForLoad(view,vfs,path))
                        {
                              setLoading(false);
                              return false;
                        }

                        // have to check again since above might set
                        // NEW_FILE flag
                        if(reload || !getFlag(NEW_FILE))
                        {
                              if(!vfs.load(view,this,path))
                              {
                                    setLoading(false);
                                    return false;
                              }
                        }
                  }
            }
            else
                  loadAutosave = false;

            //{{{ Do some stuff once loading is finished
            Runnable runnable = new Runnable()
            {
                  public void run()
                  {
                        String newPath = getStringProperty(
                              BufferIORequest.NEW_PATH);
                        Segment seg = (Segment)getProperty(
                              BufferIORequest.LOAD_DATA);
                        IntegerArray endOffsets = (IntegerArray)
                              getProperty(BufferIORequest.END_OFFSETS);

                        loadText(seg,endOffsets);

                        unsetProperty(BufferIORequest.LOAD_DATA);
                        unsetProperty(BufferIORequest.END_OFFSETS);
                        unsetProperty(BufferIORequest.NEW_PATH);

                        undoMgr.clear();
                        undoMgr.setLimit(jEdit.getIntegerProperty(
                              "buffer.undoCount",100));

                        if(!getFlag(TEMPORARY))
                              finishLoading();

                        setLoading(false);

                        // if reloading a file, clear dirty flag
                        if(reload)
                              setDirty(false);

                        if(!loadAutosave && newPath != null)
                              setPath(newPath);

                        // if loadAutosave is false, we loaded an
                        // autosave file, so we set 'dirty' to true

                        // note that we don't use setDirty(),
                        // because a) that would send an unnecessary
                        // message, b) it would also set the
                        // AUTOSAVE_DIRTY flag, which will make
                        // the autosave thread write out a
                        // redundant autosave file
                        if(loadAutosave)
                              Buffer.super.setDirty(true);

                        // send some EditBus messages
                        if(!getFlag(TEMPORARY))
                        {
                              fireBufferLoaded();
                              EditBus.send(new BufferUpdate(Buffer.this,
                                    view,BufferUpdate.LOADED));
                              //EditBus.send(new BufferUpdate(Buffer.this,
                              //    view,BufferUpdate.MARKERS_CHANGED));
                        }
                  }
            }; //}}}

            if(getFlag(TEMPORARY))
                  runnable.run();
            else
                  VFSManager.runInAWTThread(runnable);

            return true;
      } //}}}

      //{{{ insertFile() method
      /**
       * Loads a file from disk, and inserts it into this buffer.
       * @param view The view
       * @param path the path of the file to insert
       *
       * @since 4.0pre1
       */
00306       public boolean insertFile(View view, String path)
      {
            if(isPerformingIO())
            {
                  GUIUtilities.error(view,"buffer-multiple-io",null);
                  return false;
            }

            setBooleanProperty(BufferIORequest.ERROR_OCCURRED,false);

            path = MiscUtilities.constructPath(this.path,path);

            Buffer buffer = jEdit.getBuffer(path);
            if(buffer != null)
            {
                  view.getTextArea().setSelectedText(
                        buffer.getText(0,buffer.getLength()));
                  return true;
            }

            VFS vfs = VFSManager.getVFSForPath(path);

            // this returns false if initial sanity
            // checks (if the file is a directory, etc)
            // fail
            return vfs.insert(view,this,path);
      } //}}}

      //{{{ autosave() method
      /**
       * Autosaves this buffer.
       */
00338       public void autosave()
      {
            if(autosaveFile == null || !getFlag(AUTOSAVE_DIRTY)
                  || !isDirty() || isPerformingIO() ||
                  !autosaveFile.getParentFile().exists())
                  return;

            setFlag(AUTOSAVE_DIRTY,false);

            VFSManager.runInWorkThread(new BufferAutosaveRequest(
                  null,this,null,VFSManager.getFileVFS(),
                  autosaveFile.getPath()));
      } //}}}

      //{{{ saveAs() method
      /**
       * Prompts the user for a file to save this buffer to.
       * @param view The view
       * @param rename True if the buffer's path should be changed, false
       * if only a copy should be saved to the specified filename
       * @since jEdit 2.6pre5
       */
00360       public boolean saveAs(View view, boolean rename)
      {
            String[] files = GUIUtilities.showVFSFileDialog(view,path,
                  VFSBrowser.SAVE_DIALOG,false);

            // files[] should have length 1, since the dialog type is
            // SAVE_DIALOG
            if(files == null)
                  return false;

            return save(view,files[0],rename);
      } //}}}

      //{{{ save() method
      /**
       * Saves this buffer to the specified path name, or the current path
       * name if it's null.
       * @param view The view
       * @param path The path name to save the buffer to, or null to use
       * the existing path
       */
00381       public boolean save(View view, String path)
      {
            return save(view,path,true,false);
      } //}}}

      //{{{ save() method
      /**
       * Saves this buffer to the specified path name, or the current path
       * name if it's null.
       * @param view The view
       * @param path The path name to save the buffer to, or null to use
       * the existing path
       * @param rename True if the buffer's path should be changed, false
       * if only a copy should be saved to the specified filename
       * @since jEdit 2.6pre5
       */
00397       public boolean save(View view, String path, boolean rename)
      {
            return save(view,path,rename,false);
      } //}}}

      //{{{ save() method
      /**
       * Saves this buffer to the specified path name, or the current path
       * name if it's null.
       * @param view The view
       * @param path The path name to save the buffer to, or null to use
       * the existing path
       * @param rename True if the buffer's path should be changed, false
       * if only a copy should be saved to the specified filename
       * @param disableFileStatusCheck  Disables file status checking
       * regardless of the state of the checkFileStatus property
       */
00414       public boolean save(final View view, String path, final boolean rename, boolean disableFileStatusCheck)
      {
            if(isPerformingIO())
            {
                  GUIUtilities.error(view,"buffer-multiple-io",null);
                  return false;
            }

            setBooleanProperty(BufferIORequest.ERROR_OCCURRED,false);

            if(path == null && getFlag(NEW_FILE))
                  return saveAs(view,rename);

            if(path == null && file != null)
            {
                  long newModTime = file.lastModified();

                  if(newModTime != modTime
                        && jEdit.getBooleanProperty("view.checkModStatus"))
                  {
                        Object[] args = { this.path };
                        int result = GUIUtilities.confirm(view,
                              "filechanged-save",args,
                              JOptionPane.YES_NO_OPTION,
                              JOptionPane.WARNING_MESSAGE);
                        if(result != JOptionPane.YES_OPTION)
                              return false;
                  }
            }

            EditBus.send(new BufferUpdate(this,view,BufferUpdate.SAVING));

            setPerformingIO(true);

            final String oldPath = this.path;
            final String oldSymlinkPath = symlinkPath;
            final String newPath = path == null ? this.path : path;

            VFS vfs = VFSManager.getVFSForPath(newPath);

            if(!checkFileForSave(view,vfs,newPath))
            {
                  setPerformingIO(false);
                  return false;
            }

            Object session = vfs.createVFSSession(newPath,view);
            if (session == null)
            {
                  setPerformingIO(false);
                  return false;
            }

            unsetProperty("overwriteReadonly");
            unsetProperty("forbidTwoStageSave");
            try
            {
                  VFSFile file = vfs._getFile(session,newPath,view);
                  if (file != null)
                  {
                        boolean vfsRenameCap = (vfs.getCapabilities() & VFS.RENAME_CAP) != 0;
                        if (!file.isWriteable())
                        {
                              Log.log(Log.WARNING, this, "Buffer saving : File " + file + " is readOnly");
                              if (vfsRenameCap)
                              {
                                    Log.log(Log.DEBUG, this, "Buffer saving : VFS can rename files");
                                    String savePath = vfs._canonPath(session,newPath,view);
                                    if(!MiscUtilities.isURL(savePath))
                                          savePath = MiscUtilities.resolveSymlinks(savePath);
                                    savePath = vfs.getTwoStageSaveName(savePath);
                                    if (savePath == null)
                                    {
                                          Log.log(Log.DEBUG, this, "Buffer saving : two stage save impossible because path is null");
                                          VFSManager.error(view,
                                                newPath,
                                                "ioerror.save-readonly-twostagefail",
                                                null);
                                          setPerformingIO(false);
                                          return false;
                                    }
                                    else
                                    {
                                          int result = GUIUtilities.confirm(
                                                view, "vfs.overwrite-readonly",
                                                new Object[]{newPath},
                                                JOptionPane.YES_NO_OPTION,
                                                JOptionPane.WARNING_MESSAGE);
                                          if (result == JOptionPane.YES_OPTION)
                                          {
                                                Log.log(Log.WARNING, this, "Buffer saving : two stage save will be used to save buffer");
                                                setBooleanProperty("overwriteReadonly",true);
                                          }
                                          else
                                          {
                                                Log.log(Log.DEBUG,this, "Buffer not saved");
                                                setPerformingIO(false);
                                                return false;
                                          }
                                    }
                              }
                              else
                              {
                                    Log.log(Log.WARNING, this, "Buffer saving : file is readonly and vfs cannot do two stage save");
                                    VFSManager.error(view,
                                          newPath,
                                          "ioerror.write-error-readonly",
                                          null);
                                    setPerformingIO(false);
                                    return false;
                              }
                        }
                        else
                        {
                              String savePath = vfs._canonPath(session,newPath,view);
                              if(!MiscUtilities.isURL(savePath))
                                    savePath = MiscUtilities.resolveSymlinks(savePath);
                              savePath = vfs.getTwoStageSaveName(savePath);
                              if (jEdit.getBooleanProperty("twoStageSave") && (!vfsRenameCap || savePath == null))
                              {
                                    // the file is writeable but the vfs cannot do two stage. We must overwrite
                                    // readonly flag


                                    int result = GUIUtilities.confirm(
                                                view, "vfs.twostageimpossible",
                                                new Object[]{newPath},
                                                JOptionPane.YES_NO_OPTION,
                                                JOptionPane.WARNING_MESSAGE);
                                    if (result == JOptionPane.YES_OPTION)
                                    {
                                          Log.log(Log.WARNING, this, "Buffer saving : two stage save cannot be used");
                                          setBooleanProperty("forbidTwoStageSave",true);
                                    }
                                    else
                                    {
                                          Log.log(Log.DEBUG,this, "Buffer not saved");
                                          setPerformingIO(false);
                                          return false;
                                    }

                              }
                        }
                  }
            }
            catch(IOException io)
            {
                  VFSManager.error(view,newPath,"ioerror",
                        new String[] { io.toString() });
                  setPerformingIO(false);
                  return false;
            }
            finally
            {
                  try
                  {
                        vfs._endVFSSession(session,view);
                  }
                  catch(IOException io)
                  {
                        VFSManager.error(view,newPath,"ioerror",
                              new String[] { io.toString() });
                        setPerformingIO(false);
                        return false;
                  }
            }

            if(!vfs.save(view,this,newPath))
            {
                  setPerformingIO(false);
                  return false;
            }

            // Once save is complete, do a few other things
            VFSManager.runInAWTThread(new Runnable()
                  {
                        public void run()
                        {
                              setPerformingIO(false);
                              setProperty("overwriteReadonly",null);
                              finishSaving(view,oldPath,oldSymlinkPath,
                                    newPath,rename,getBooleanProperty(
                                          BufferIORequest.ERROR_OCCURRED));
                              updateMarkersFile(view);
                        }
                  });

            int check = jEdit.getIntegerProperty("checkFileStatus");
            if(!disableFileStatusCheck && (check == GeneralOptionPane.checkFileStatus_all ||
                                     check == GeneralOptionPane.checkFileStatus_operations))
                  jEdit.checkBufferStatus(view,false);

            return true;
      } //}}}

      //{{{ checkFileStatus() method
      public static final int FILE_NOT_CHANGED = 0;
      public static final int FILE_CHANGED = 1;
      public static final int FILE_DELETED = 2;
      /**
       * Check if the buffer has changed on disk.
       * @return One of <code>NOT_CHANGED</code>, <code>CHANGED</code>, or
       * <code>DELETED</code>.
       *
       * @since jEdit 4.2pre1
       */
00620       public int checkFileStatus(View view)
      {
            // - don't do these checks while a save is in progress,
            // because for a moment newModTime will be greater than
            // oldModTime, due to the multithreading
            // - only supported on local file system
            if(!isPerformingIO() && file != null && !getFlag(NEW_FILE))
            {
                  boolean newReadOnly = file.exists() && !file.canWrite();
                  if(newReadOnly != isFileReadOnly())
                  {
                        setFileReadOnly(newReadOnly);
                        EditBus.send(new BufferUpdate(this,null,
                              BufferUpdate.DIRTY_CHANGED));
                  }

                  long oldModTime = modTime;
                  long newModTime = file.lastModified();

                  if(newModTime != oldModTime)
                  {
                        modTime = newModTime;

                        if(!file.exists())
                        {
                              setFlag(NEW_FILE,true);
                              setDirty(true);
                              return FILE_DELETED;
                        }
                        else
                        {
                              return FILE_CHANGED;
                        }
                  }
            }

            return FILE_NOT_CHANGED;
      } //}}}

      //}}}

      //{{{ Getters/setter methods for various buffer meta-data

      //{{{ getLastModified() method
      /**
       * Returns the last time jEdit modified the file on disk.
       * This method is thread-safe.
       */
00668       public long getLastModified()
      {
            return modTime;
      } //}}}

      //{{{ setLastModified() method
      /**
       * Sets the last time jEdit modified the file on disk.
       * @param modTime The new modification time
       */
00678       public void setLastModified(long modTime)
      {
            this.modTime = modTime;
      } //}}}

      //{{{ getAutoReload() method
      /**
       * Returns the status of the AUTORELOAD flag
       * If true, reload changed files automatically
       */
00688       public boolean getAutoReload()
      {
            return getFlag(AUTORELOAD);
      } //}}}

      //{{{ setAutoReload() method
      /**
       * Sets the status of the AUTORELOAD flag
       * @param value # If true, reload changed files automatically
       */
00698       public void setAutoReload(boolean value)
      {
            setFlag(AUTORELOAD, value);
      } //}}}

      //{{{ getAutoReloadDialog() method
      /**
       * Returns the status of the AUTORELOAD_DIALOG flag
       * If true, prompt for reloading or notify user
       * when the file has changed on disk
       */
00709       public boolean getAutoReloadDialog()
      {
            return getFlag(AUTORELOAD_DIALOG);
      } //}}}

      //{{{ setAutoReloadDialog() method
      /**
       * Sets the status of the AUTORELOAD_DIALOG flag
       * @param value # If true, prompt for reloading or notify user
       * when the file has changed on disk

       */
00721       public void setAutoReloadDialog(boolean value)
      {
            setFlag(AUTORELOAD_DIALOG, value);
      } //}}}

      //{{{ getVFS() method
      /**
       * Returns the virtual filesystem responsible for loading and
       * saving this buffer. This method is thread-safe.
       */
00731       public VFS getVFS()
      {
            return VFSManager.getVFSForPath(path);
      } //}}}

      //{{{ getAutosaveFile() method
      /**
       * Returns the autosave file for this buffer. This may be null if
       * the file is non-local.
       */
00741       public File getAutosaveFile()
      {
            return autosaveFile;
      } //}}}

      //{{{ removeAutosaveFile() method
      /**
       * Remove the autosave file.
       * @since jEdit 4.3pre12
       */
00751       public void removeAutosaveFile()
      {
            if (autosaveFile != null)
            {
                  autosaveFile.delete();
                  setFlag(AUTOSAVE_DIRTY,true);
            }
      } //}}}

      //{{{ getName() method
      /**
       * Returns the name of this buffer. This method is thread-safe.
       */
00764       public String getName()
      {
            return name;
      } //}}}

      //{{{ getPath() method
      /**
       * Returns the path name of this buffer. This method is thread-safe.
       */
00773       public String getPath()
      {
            return path;
      } //}}}

      //{{{ getPath() method
      /**
        * @param shortVersion if true, replaces home path with ~/ on unix
        */
00782       public String getPath(Boolean shortVersion)
      {
            return shortVersion ? MiscUtilities.abbreviate(path) : getPath();
      } //}}}


      //{{{ getSymlinkPath() method
      /**
       * If this file is a symbolic link, returns the link destination.
       * Otherwise returns the file's path. This method is thread-safe.
       * @since jEdit 4.2pre1
       */
00794       public String getSymlinkPath()
      {
            return symlinkPath;
      } //}}}

      //{{{ getDirectory() method
      /**
       * Returns the directory containing this buffer.
       * @since jEdit 4.1pre11
       */
00804       public String getDirectory()
      {
            return directory;
      } //}}}

      //{{{ isClosed() method
      /**
       * Returns true if this buffer has been closed with
       * {@link org.gjt.sp.jedit.jEdit#closeBuffer(View,Buffer)}.
       * This method is thread-safe.
       */
00815       public boolean isClosed()
      {
            return getFlag(CLOSED);
      } //}}}

      //{{{ isLoaded() method
      /**
       * Returns true if the buffer is loaded. This method is thread-safe.
       */
00824       public boolean isLoaded()
      {
            return !isLoading();
      } //}}}

      //{{{ isNewFile() method
      /**
       * Returns whether this buffer lacks a corresponding version on disk.
       * This method is thread-safe.
       */
00834       public boolean isNewFile()
      {
            return getFlag(NEW_FILE);
      } //}}}

      //{{{ setNewFile() method
      /**
       * Sets the new file flag.
       * @param newFile The new file flag
       */
00844       public void setNewFile(boolean newFile)
      {
            setFlag(NEW_FILE,newFile);
            if(!newFile)
                  setFlag(UNTITLED,false);
      } //}}}

      //{{{ isUntitled() method
      /**
       * Returns true if this file is 'untitled'. This method is thread-safe.
       */
00855       public boolean isUntitled()
      {
            return getFlag(UNTITLED);
      } //}}}

      //{{{ setDirty() method
      /**
       * Sets the 'dirty' (changed since last save) flag of this buffer.
       */
      @Override
00865       public void setDirty(boolean d)
      {
            boolean old_d = isDirty();
            if (isUntitled() && jEdit.getBooleanProperty("suppressNotSavedConfirmUntitled"))
                  d = false;
            if (d && getLength() == initialLength)
            {
                  if (jEdit.getBooleanProperty("useMD5forDirtyCalculation")) 
                        d = !Arrays.equals(calculateHash(), md5hash);
            }
            super.setDirty(d);
            boolean editable = isEditable();

            if(d)
            {
                  if(editable)
                        setFlag(AUTOSAVE_DIRTY,true);
            }
            else
            {
                  setFlag(AUTOSAVE_DIRTY,false);

                  if(autosaveFile != null)
                        autosaveFile.delete();
            }

            if(d != old_d && editable)
            {
                  EditBus.send(new BufferUpdate(this,null,
                        BufferUpdate.DIRTY_CHANGED));
            }
      } //}}}

      //{{{ isTemporary() method
      /**
       * Returns if this is a temporary buffer. This method is thread-safe.
       * @see jEdit#openTemporary(View,String,String,boolean)
       * @see jEdit#commitTemporary(Buffer)
       * @since jEdit 2.2pre7
       */
00905       public boolean isTemporary()
      {
            return getFlag(TEMPORARY);
      } //}}}

      //{{{ getIcon() method
      /**
       * Returns this buffer's icon.
       * @since jEdit 2.6pre6
       */
00915       public Icon getIcon()
      {
            if(isDirty())
                  return GUIUtilities.loadIcon("dirty.gif");
            else if(isReadOnly())
                  return GUIUtilities.loadIcon("readonly.gif");
            else if(getFlag(NEW_FILE))
                  return GUIUtilities.loadIcon("new.gif");
            else
                  return GUIUtilities.loadIcon("normal.gif");
      } //}}}

      //}}}

      //{{{ Buffer events

      //{{{ addBufferChangeListener() method
      /**
       * @deprecated Call {@link JEditBuffer#addBufferListener(BufferListener,int)}.
       */
      @Deprecated
00936       public void addBufferChangeListener(BufferChangeListener listener,
            int priority)
      {
            addBufferListener(new BufferChangeListener.Adapter(listener),priority);
      } //}}}

      //{{{ addBufferChangeListener() method
      /**
       * @deprecated Call {@link JEditBuffer#addBufferListener(BufferListener)}.
       */
      @Deprecated
00947       public void addBufferChangeListener(BufferChangeListener listener)
      {
            addBufferListener(new BufferChangeListener.Adapter(listener), NORMAL_PRIORITY);
      } //}}}

      //{{{ removeBufferChangeListener() method
      /**
       * @deprecated Call {@link JEditBuffer#removeBufferListener(BufferListener)}.
       */
      @Deprecated
00957       public void removeBufferChangeListener(BufferChangeListener listener)
      {
            BufferListener[] listeners = getBufferListeners();

            for(int i = 0; i < listeners.length; i++)
            {
                  BufferListener l = listeners[i];
                  if(l instanceof BufferChangeListener.Adapter)
                  {
                        if(((BufferChangeListener.Adapter)l).getDelegate() == listener)
                        {
                              removeBufferListener(l);
                              return;
                        }
                  }
            }
      } //}}}

      //}}}

      //{{{ Property methods

      //{{{ propertiesChanged() method
      /**
       * Reloads settings from the properties. This should be called
       * after the <code>syntax</code> or <code>folding</code>
       * buffer-local properties are changed.
       */
      @Override
00986       public void propertiesChanged()
      {
            super.propertiesChanged();
            setAutoReloadDialog(jEdit.getBooleanProperty("autoReloadDialog"));
            setAutoReload(jEdit.getBooleanProperty("autoReload"));
            EditBus.send(new BufferUpdate(this,null,BufferUpdate.PROPERTIES_CHANGED));
      } //}}}

      //{{{ getDefaultProperty() method
      @Override
      public Object getDefaultProperty(String name)
      {
            Object retVal;

            if(mode != null)
            {
                  retVal = mode.getProperty(name);
                  if(retVal == null)
                        return null;

                  setDefaultProperty(name,retVal);
                  return retVal;
            }
            // Now try buffer.<property>
            String value = jEdit.getProperty("buffer." + name);
            if(value == null)
                  return null;

            // Try returning it as an integer first
            try
            {
                  retVal = new Integer(value);
            }
            catch(NumberFormatException nf)
            {
                  retVal = value;
            }

            return retVal;
      } //}}}

      //{{{ toggleWordWrap() method
      /**
       * Toggles word wrap between the three available modes. This is used
       * by the status bar.
       * @param view We show a message in the view's status bar
       * @since jEdit 4.1pre3
       */
01034       public void toggleWordWrap(View view)
      {
            String wrap = getStringProperty("wrap");
            if(wrap.equals("none"))
                  wrap = "soft";
            else if(wrap.equals("soft"))
                  wrap = "hard";
            else if(wrap.equals("hard"))
                  wrap = "none";
            view.getStatus().setMessageAndClear(jEdit.getProperty(
                  "view.status.wrap-changed",new String[] {
                  wrap }));
            setProperty("wrap",wrap);
            propertiesChanged();
      } //}}}

      //{{{ toggleLineSeparator() method
      /**
       * Toggles the line separator between the three available settings.
       * This is used by the status bar.
       * @param view We show a message in the view's status bar
       * @since jEdit 4.1pre3
       */
01057       public void toggleLineSeparator(View view)
      {
            String status = null;
            String lineSep = getStringProperty(LINESEP);
            if("\n".equals(lineSep))
            {
                  status = "windows";
                  lineSep = "\r\n";
            }
            else if("\r\n".equals(lineSep))
            {
                  status = "mac";
                  lineSep = "\r";
            }
            else if("\r".equals(lineSep))
            {
                  status = "unix";
                  lineSep = "\n";
            }
            view.getStatus().setMessageAndClear(jEdit.getProperty(
                  "view.status.linesep-changed",new String[] {
                  jEdit.getProperty("lineSep." + status) }));
            setProperty(LINESEP, lineSep);
            setDirty(true);
            propertiesChanged();
      } //}}}

      //{{{ getContextSensitiveProperty() method
      /**
       * Some settings, like comment start and end strings, can
       * vary between different parts of a buffer (HTML text and inline
       * JavaScript, for example).
       * @param offset The offset
       * @param name The property name
       * @since jEdit 4.0pre3
       */
      @Override
01094       public String getContextSensitiveProperty(int offset, String name)
      {
            Object value = super.getContextSensitiveProperty(offset,name);

            if(value == null)
            {
                  ParserRuleSet rules = getRuleSetAtOffset(offset);

                  value = jEdit.getMode(rules.getModeName())
                        .getProperty(name);

                  if(value == null)
                        value = mode.getProperty(name);
            }

            if(value == null)
                  return null;
            else
                  return String.valueOf(value);
      } //}}}

      //}}}

      //{{{ Edit modes, syntax highlighting

      //{{{ setMode() method
      /**
       * Sets this buffer's edit mode by calling the accept() method
       * of each registered edit mode.
       */
01124       public void setMode()
      {
            String userMode = getStringProperty("mode");
            if(userMode != null)
            {
                  unsetProperty("mode");
                  Mode m = ModeProvider.instance.getMode(userMode);
                  if(m != null)
                  {
                        setMode(m);
                        return;
                  }
            }

            String firstLine = getLineText(0);

            Mode mode = ModeProvider.instance.getModeForFile(name, firstLine);
            if (mode != null)
            {
                  setMode(mode);
                  return;
            }

            Mode defaultMode = jEdit.getMode(jEdit.getProperty("buffer.defaultMode"));
            if(defaultMode == null)
                  defaultMode = jEdit.getMode("text");

            if (defaultMode != null)
                  setMode(defaultMode);
      } //}}}

      //}}}

      //{{{ Deprecated methods

      //{{{ putProperty() method
      /**
       * @deprecated Call <code>setProperty()</code> instead.
       */
      @Deprecated
01164       public void putProperty(Object name, Object value)
      {
            // for backwards compatibility
            if(!(name instanceof String))
                  return;

            setProperty((String)name,value);
      } //}}}

      //{{{ putBooleanProperty() method
      /**
       * @deprecated Call <code>setBooleanProperty()</code> instead
       */
      @Deprecated
01178       public void putBooleanProperty(String name, boolean value)
      {
            setBooleanProperty(name,value);
      } //}}}

      //{{{ markTokens() method
      /**
       * @deprecated Use org.gjt.sp.jedit.syntax.DefaultTokenHandler instead
       */
      @Deprecated
01188       public static class TokenList extends DefaultTokenHandler
      {
            public Token getFirstToken()
            {
                  return getTokens();
            }
      }

      /**
       * @deprecated Use the other form of <code>markTokens()</code> instead
       */
      @Deprecated
01200       public TokenList markTokens(int lineIndex)
      {
            TokenList list = new TokenList();
            markTokens(lineIndex,list);
            return list;
      } //}}}

      //{{{ insertString() method
      /**
       * Insert a string into the buffer
       * @param offset The offset
       * @param str The string
       * @param attr ignored
       * @deprecated Call <code>insert()</code> instead.
       */
      @Deprecated
01216       public void insertString(int offset, String str, AttributeSet attr)
      {
            insert(offset,str);
      } //}}}

      //{{{ getFile() method
      /**
       * @deprecated Do not call this method, use {@link #getPath()}
       * instead.
       */
      @Deprecated
01227       public File getFile()
      {
            return file;
      } //}}}

      //}}}

      //{{{ Marker methods

      //{{{ getMarkers() method
      /**
       * Returns a vector of markers.
       * @since jEdit 3.2pre1
       */
01241       public Vector<Marker> getMarkers()
      {
            return markers;
      } //}}}

      //{{{ getMarkerStatusPrompt() method
      /**
       * Returns the status prompt for the given marker action. Only
       * intended to be called from <code>actions.xml</code>.
       * @since jEdit 4.2pre2
       */
01252       public String getMarkerStatusPrompt(String action)
      {
            return jEdit.getProperty("view.status." + action,
                  new String[] { getMarkerNameString() });
      } //}}}

      //{{{ getMarkerNameString() method
      /**
       * Returns a string of all set markers, used by the status bar
       * (eg, "a b $ % ^").
       * @since jEdit 4.2pre2
       */
01264       public String getMarkerNameString()
      {
            StringBuilder buf = new StringBuilder();
            for(int i = 0; i < markers.size(); i++)
            {
                  Marker marker = markers.get(i);
                  if(marker.getShortcut() != '\0')
                  {
                        if(buf.length() != 0)
                              buf.append(' ');
                        buf.append(marker.getShortcut());
                  }
            }

            if(buf.length() == 0)
                  return jEdit.getProperty("view.status.no-markers");
            else
                  return buf.toString();
      } //}}}

      //{{{ addOrRemoveMarker() method
      /**
       * If a marker is set on the line of the position, it is removed. Otherwise
       * a new marker with the specified shortcut is added.
       * @param pos The position of the marker
       * @param shortcut The shortcut ('\0' if none)
       * @since jEdit 3.2pre5
       */
01292       public void addOrRemoveMarker(char shortcut, int pos)
      {
            int line = getLineOfOffset(pos);
            if(getMarkerAtLine(line) != null)
                  removeMarker(line);
            else
                  addMarker(shortcut,pos);
      } //}}}

      //{{{ addMarker() method
      /**
       * Adds a marker to this buffer.
       * @param pos The position of the marker
       * @param shortcut The shortcut ('\0' if none)
       * @since jEdit 3.2pre1
       */
01308       public void addMarker(char shortcut, int pos)
      {
            Marker markerN = new Marker(this,shortcut,pos);
            boolean added = false;

            // don't sort markers while buffer is being loaded
            if(isLoaded())
            {
                  setFlag(MARKERS_CHANGED,true);

                  markerN.createPosition();

                  for(int i = 0; i < markers.size(); i++)
                  {
                        Marker marker = markers.get(i);
                        if(shortcut != '\0' && marker.getShortcut() == shortcut)
                              marker.setShortcut('\0');

                        if(marker.getPosition() == pos)
                        {
                              markers.removeElementAt(i);
                              i--;
                        }
                  }

                  for(int i = 0; i < markers.size(); i++)
                  {
                        Marker marker = markers.get(i);
                        if(marker.getPosition() > pos)
                        {
                              markers.insertElementAt(markerN,i);
                              added = true;
                              break;
                        }
                  }
            }

            if(!added)
                  markers.addElement(markerN);

            if(isLoaded() && !getFlag(TEMPORARY))
            {
                  EditBus.send(new BufferUpdate(this,null,
                        BufferUpdate.MARKERS_CHANGED));
            }
      } //}}}

      //{{{ getMarkerInRange() method
      /**
       * Returns the first marker within the specified range.
       * @param start The start offset
       * @param end The end offset
       * @since jEdit 4.0pre4
       */
01362       public Marker getMarkerInRange(int start, int end)
      {
            for(int i = 0; i < markers.size(); i++)
            {
                  Marker marker = markers.get(i);
                  int pos = marker.getPosition();
                  if(pos >= start && pos < end)
                        return marker;
            }

            return null;
      } //}}}

      //{{{ getMarkerAtLine() method
      /**
       * Returns the first marker at the specified line, or <code>null</code>
       * if there is none.
       * @param line The line number
       * @since jEdit 3.2pre2
       */
01382       public Marker getMarkerAtLine(int line)
      {
            for(int i = 0; i < markers.size(); i++)
            {
                  Marker marker = markers.get(i);
                  if(getLineOfOffset(marker.getPosition()) == line)
                        return marker;
            }

            return null;
      } //}}}

      //{{{ removeMarker() method
      /**
       * Removes all markers at the specified line.
       * @param line The line number
       * @since jEdit 3.2pre2
       */
01400       public void removeMarker(int line)
      {
            for(int i = 0; i < markers.size(); i++)
            {
                  Marker marker = markers.get(i);
                  if(getLineOfOffset(marker.getPosition()) == line)
                  {
                        setFlag(MARKERS_CHANGED,true);
                        marker.removePosition();
                        markers.removeElementAt(i);
                        i--;
                  }
            }

            EditBus.send(new BufferUpdate(this,null,
                  BufferUpdate.MARKERS_CHANGED));
      } //}}}

      //{{{ removeAllMarkers() method
      /**
       * Removes all defined markers.
       * @since jEdit 2.6pre1
       */
01423       public void removeAllMarkers()
      {
            setFlag(MARKERS_CHANGED,true);

            for(int i = 0; i < markers.size(); i++)
                  markers.get(i).removePosition();

            markers.removeAllElements();

            if(isLoaded())
            {
                  EditBus.send(new BufferUpdate(this,null,
                        BufferUpdate.MARKERS_CHANGED));
            }
      } //}}}

      //{{{ getMarker() method
      /**
       * Returns the marker with the specified shortcut.
       * @param shortcut The shortcut
       * @since jEdit 3.2pre2
       */
01445       public Marker getMarker(char shortcut)
      {
            for (Marker marker : markers)
            {
                  if(marker.getShortcut() == shortcut)
                        return marker;
            }
            return null;
      } //}}}

      //{{{ getMarkersPath() method
      /**
       * Returns the path for this buffer's markers file
       * @param vfs The appropriate VFS
       * @since jEdit 4.3pre7
       * @deprecated it will fail if you save to another VFS. use {@link #getMarkersPath(VFS, String)}
       */
      @Deprecated
01463       public String getMarkersPath(VFS vfs)
      {
            return getMarkersPath(vfs, path);
      } //}}}

      //{{{ getMarkersPath() method
      /**
       * Returns the path for this buffer's markers file
       * @param vfs The appropriate VFS
       * @param path the path of the buffer, it can be different from the field
       * when using save-as
       * @since jEdit 4.3pre10
       */
01476       public static String getMarkersPath(VFS vfs, String path)
      {
            return vfs.getParentOfPath(path)
                  + '.' + vfs.getFileName(path)
                  + ".marks";
      } //}}}

      //{{{ updateMarkersFile() method
      /**
       * Save the markers file, or delete it when there are mo markers left
       * Handling markers is now independent from saving the buffer.
       * Changing markers will not set the buffer dirty any longer.
       * @param view The current view
       * @since jEdit 4.3pre7
       */
01491       public boolean updateMarkersFile(View view)
      {
            if(!markersChanged())
                  return true;
            // adapted from VFS.save
            VFS vfs = VFSManager.getVFSForPath(getPath());
            if (((vfs.getCapabilities() & VFS.WRITE_CAP) == 0) ||
                !vfs.isMarkersFileSupported())
            {
                  VFSManager.error(view, path, "vfs.not-supported.save",
                        new String[] { "markers file" });
                  return false;
            }
            Object session = vfs.createVFSSession(path, view);
            if(session == null)
                  return false;
            VFSManager.runInWorkThread(
                  new MarkersSaveRequest(
                        view, this, session, vfs, path));
            return true;
      } //}}}

      //{{{ markersChanged() method
      /**
       * Return true when markers have changed and the markers file needs
       * to be updated
       * @since jEdit 4.3pre7
       */
01519       public boolean markersChanged()
      {
            return getFlag(MARKERS_CHANGED);
      } //}}}

      //{{{ setMarkersChanged() method
      /**
       * Sets/unsets the MARKERS_CHANGED flag
       * @since jEdit 4.3pre7
       */
01529       public void setMarkersChanged(boolean changed)
      {
            setFlag(MARKERS_CHANGED, changed);
      } //}}}

      //}}}

      //{{{ Miscellaneous methods

      //{{{ setWaitSocket() method
      /**
       * This socket is closed when the buffer is closed.
       */
01542       public void setWaitSocket(Socket waitSocket)
      {
            this.waitSocket = waitSocket;
      } //}}}

      //{{{ getNext() method
      /**
       * Returns the next buffer in the list.
       */
01551       public Buffer getNext()
      {
            return next;
      } //}}}

      //{{{ getPrev() method
      /**
       * Returns the previous buffer in the list.
       */
01560       public Buffer getPrev()
      {
            return prev;
      } //}}}

      //{{{ getIndex() method
      /**
       * Returns the position of this buffer in the buffer list.
       */
01569       public int getIndex()
      {
            int count = 0;
            Buffer buffer = prev;
            while (true)
            {
                  if(buffer == null)
                        break;
                  count++;
                  buffer = buffer.prev;
            }
            return count;
      } //}}}

      //{{{ toString() method
      /**
       * Returns a string representation of this buffer.
       * This simply returns the path name.
       */
      @Override
01589       public String toString()
      {
            return name + " (" + MiscUtilities.abbreviate(directory) + ')';
      } //}}}

      //}}}

      //{{{ Package-private members
      /** The previous buffer in the list. */
01598       Buffer prev;
      /** The next buffer in the list. */
01600       Buffer next;

      //{{{ Buffer constructor
      Buffer(String path, boolean newFile, boolean temp, Map props)
      {
            super(props);

            markers = new Vector<Marker>();

            setFlag(TEMPORARY,temp);

            // this must be called before any EditBus messages are sent
            setPath(path);

            /* Magic: UNTITLED is only set if newFile param to
             * constructor is set, NEW_FILE is also set if file
             * doesn't exist on disk.
             *
             * This is so that we can tell apart files created
             * with jEdit.newFile(), and those that just don't
             * exist on disk.
             *
             * Why do we need to tell the difference between the
             * two? jEdit.addBufferToList() checks if the only
             * opened buffer is an untitled buffer, and if so,
             * replaces it with the buffer to add. We don't want
             * this behavior to occur with files that don't
             * exist on disk; only untitled ones.
             */
            setFlag(UNTITLED,newFile);
            setFlag(NEW_FILE,newFile);
            setFlag(AUTORELOAD,jEdit.getBooleanProperty("autoReload"));
            setFlag(AUTORELOAD_DIALOG,jEdit.getBooleanProperty("autoReloadDialog"));
      } //}}}

      //{{{ commitTemporary() method
      void commitTemporary()
      {
            setFlag(TEMPORARY,false);

            finishLoading();
      } //}}}

      //{{{ close() method
      void close()
      {
            setFlag(CLOSED,true);

            if(autosaveFile != null)
                  autosaveFile.delete();

            // notify clients with -wait
            if(waitSocket != null)
            {
                  try
                  {
                        waitSocket.getOutputStream().write('\0');
                        waitSocket.getOutputStream().flush();
                        waitSocket.getInputStream().close();
                        waitSocket.getOutputStream().close();
                        waitSocket.close();
                  }
                  catch(IOException io)
                  {
                        //Log.log(Log.ERROR,this,io);
                  }
            }
      } //}}}

      //}}}

      //{{{ Private members

      //{{{ Flags

      //{{{ setFlag() method
      private void setFlag(int flag, boolean value)
      {
            if(value)
                  flags |= 1 << flag;
            else
                  flags &= ~(1 << flag);
      } //}}}

      //{{{ getFlag() method
      private boolean getFlag(int flag)
      {
            int mask = 1 << flag;
            return (flags & mask) == mask;
      } //}}}

      //{{{ Flag values
      private static final int CLOSED = 0;
      private static final int NEW_FILE = 3;
      private static final int UNTITLED = 4;
      private static final int AUTOSAVE_DIRTY = 5;
      private static final int AUTORELOAD = 6;
      private static final int AUTORELOAD_DIALOG = 7;
      private static final int TEMPORARY = 10;
      private static final int MARKERS_CHANGED = 12;
      //}}}

      private int flags;

      //}}}

      //{{{ Instance variables
      private String path;
      private String symlinkPath;
      private String name;
      private String directory;
      private File file;
      private File autosaveFile;
      private long modTime;
      private byte[] md5hash;
      private int initialLength;

      private final Vector<Marker> markers;

      private Socket waitSocket;
      //}}}

      //{{{ setPath() method
      private void setPath(final String path)
      {
            jEdit.visit(new JEditVisitorAdapter()
            {
                  @Override
                  public void visit(EditPane editPane)
                  {
                        editPane.bufferRenamed(Buffer.this.path, path);
                  }
            });

            this.path = path;
            VFS vfs = VFSManager.getVFSForPath(path);
            if((vfs.getCapabilities() & VFS.WRITE_CAP) == 0)
                  setFileReadOnly(true);
            name = vfs.getFileName(path);
            directory = vfs.getParentOfPath(path);

            if(vfs instanceof FileVFS)
            {
                  file = new File(path);
                  symlinkPath = MiscUtilities.resolveSymlinks(path);

                  // if we don't do this, the autosave file won't be
                  // deleted after a save as
                  if(autosaveFile != null)
                        autosaveFile.delete();
                  autosaveFile = new File(file.getParent(),'#' + name + '#');
            }
            else
            {
                  // I wonder if the lack of this broke anything in the
                  // past?
                  file = null;
                  autosaveFile = null;
                  symlinkPath = path;
            }
      } //}}}


      //{{{ recoverAutosave() method
      private boolean recoverAutosave(final View view)
      {
            if(!autosaveFile.canRead())
                  return false;

            // this method might get called at startup
            GUIUtilities.hideSplashScreen();

            final Object[] args = { autosaveFile.getPath() };
            int result = GUIUtilities.confirm(view,"autosave-found",args,
                  JOptionPane.YES_NO_OPTION,JOptionPane.WARNING_MESSAGE);

            if(result == JOptionPane.YES_OPTION)
            {
                  VFSManager.getFileVFS().load(view,this,autosaveFile.getPath());

                  // show this message when all I/O requests are
                  // complete
                  VFSManager.runInAWTThread(new Runnable()
                  {
                        public void run()
                        {
                              GUIUtilities.message(view,"autosave-loaded",args);
                        }
                  });

                  return true;
            }
            else
                  return false;
      } //}}}

      //{{{ checkFileForLoad() method
      private boolean checkFileForLoad(View view, VFS vfs, String path)
      {
            if((vfs.getCapabilities() & VFS.LOW_LATENCY_CAP) != 0)
            {
                  Object session = vfs.createVFSSession(path, view);
                  if(session == null)
                        return false;

                  try
                  {
                        VFSFile file = vfs._getFile(session,path,view);
                        if(file == null)
                        {
                              setNewFile(true);
                              return true;
                        }

                        if(!file.isReadable())
                        {
                              VFSManager.error(view,path,"ioerror.no-read",null);
                              setNewFile(false);
                              return false;
                        }

                        setFileReadOnly(!file.isWriteable());

                        if(file.getType() != VFSFile.FILE)
                        {
                              VFSManager.error(view,path,
                                    "ioerror.open-directory",null);
                              setNewFile(false);
                              return false;
                        }
                  }
                  catch(IOException io)
                  {
                        VFSManager.error(view,path,"ioerror",
                              new String[] { io.toString() });
                        return false;
                  }
                  finally
                  {
                        try
                        {
                              vfs._endVFSSession(session,view);
                        }
                        catch(IOException io)
                        {
                              VFSManager.error(view,path,"ioerror",
                                    new String[] { io.toString() });
                              return false;
                        }
                  }
            }

            return true;
      } //}}}

      //{{{ checkFileForSave() method
      private static boolean checkFileForSave(View view, VFS vfs, String path)
      {
            if((vfs.getCapabilities() & VFS.LOW_LATENCY_CAP) != 0)
            {
                  Object session = vfs.createVFSSession(path,view);
                  if(session == null)
                        return false;

                  try
                  {
                        VFSFile file = vfs._getFile(session,path,view);
                        if(file == null)
                              return true;

                        if(file.getType() != VFSFile.FILE)
                        {
                              VFSManager.error(view,path,
                                    "ioerror.save-directory",null);
                              return false;
                        }
                  }
                  catch(IOException io)
                  {
                        VFSManager.error(view,path,"ioerror",
                              new String[] { io.toString() });
                        return false;
                  }
                  finally
                  {
                        try
                        {
                              vfs._endVFSSession(session,view);
                        }
                        catch(IOException io)
                        {
                              VFSManager.error(view,path,"ioerror",
                                    new String[] { io.toString() });
                              return false;
                        }
                  }
            }

            return true;
      } //}}}

      /** @return an MD5 hash of the contents of the buffer */
01902       private byte[] calculateHash()
      {
            final byte[] dummy = new byte[1]; 
            if (!jEdit.getBooleanProperty("useMD5forDirtyCalculation"))
                  return dummy;
            ByteBuffer bb = null;
            readLock();
            try 
            {
                  // Log.log(Log.NOTICE, this, "calculateHash()");
                  int length = getLength();
                  bb = ByteBuffer.allocate(length * 2);     // Chars are 2 bytes
                  CharBuffer cb = bb.asCharBuffer();
                  cb.append( getSegment(0, length) );
            }
            finally 
            {
                  readUnlock();
            } 
            try 
            {
                  MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
                  digest.update( bb );
                  return digest.digest();
            }
            catch (NoSuchAlgorithmException nsae) 
            {
                  Log.log(Log.ERROR, this, "Can't Calculate MD5 hash!", nsae);
                  return dummy;
            }
            
      }
      
      /** Update the buffer's members with the current hash and length,
       *  for later comparison.
       */
01938       private void updateHash() 
      {
            initialLength = getLength();
            md5hash = calculateHash();
      }
      
      //{{{ finishLoading() method
      private void finishLoading()
      {
            updateHash();
                  
            parseBufferLocalProperties();
            // AHA!
            // this is probably the only way to fix this
            FoldHandler oldFoldHandler = getFoldHandler();
            setMode();

            if(getFoldHandler() == oldFoldHandler)
            {
                  // on a reload, the fold handler doesn't change, but
                  // we still need to re-collapse folds.
                  // don't do this on initial fold handler creation
                  invalidateFoldLevels();

                  fireFoldHandlerChanged();
            }

            // Create marker positions
            for(int i = 0; i < markers.size(); i++)
            {
                  Marker marker = markers.get(i);
                  marker.removePosition();
                  int pos = marker.getPosition();
                  if(pos > getLength())
                        marker.setPosition(getLength());
                  else if(pos < 0)
                        marker.setPosition(0);
                  marker.createPosition();
            }
      } //}}}

      //{{{ finishSaving() method
      private void finishSaving(View view, String oldPath,
            String oldSymlinkPath, String path,
            boolean rename, boolean error)
      {
            
            //{{{ Set the buffer's path
            // Caveat: won't work if save() called with a relative path.
            // But I don't think anyone calls it like that anyway.
            if(!error && !path.equals(oldPath))
            {
                  Buffer buffer = jEdit.getBuffer(path);

                  if(rename)
                  {
                        /* if we save a file with the same name as one
                         * that's already open, we presume that we can
                         * close the existing file, since the user
                         * would have confirmed the overwrite in the
                         * 'save as' dialog box anyway */
                        if(buffer != null && /* can't happen? */
                              !buffer.getPath().equals(oldPath))
                        {
                              buffer.setDirty(false);
                              jEdit.closeBuffer(view,buffer);
                        }

                        setPath(path);
                        final HashSet<BufferSet> bufferSets = new HashSet<BufferSet>();
                        final HashSet<EditPane> editPanesCurrent = new HashSet<EditPane>();
                        jEdit.visit(new JEditVisitorAdapter()
                        {
                              @Override
                              public void visit(EditPane editPane)
                              {
                                    BufferSet bufferSet = editPane.getBufferSet(); 
                                    if (bufferSet.indexOf(Buffer.this) != -1)
                                    {
                                          bufferSets.add(bufferSet);
                                          if (editPane.getBuffer() == Buffer.this)
                                                editPanesCurrent.add(editPane);
                                    }
                              }
                        });
                        jEdit.getBufferSetManager().removeBuffer(this);
                        for (BufferSet bufferSet: bufferSets)
                              jEdit.getBufferSetManager().addBuffer(bufferSet, this);
                        for (EditPane editPane: editPanesCurrent)
                              editPane.setBuffer(this);
                  }
                  else
                  {
                        /* if we saved over an already open file using
                         * 'save a copy as', then reload the existing
                         * buffer */
                        if(buffer != null && /* can't happen? */
                              !buffer.getPath().equals(oldPath))
                        {
                              buffer.load(view,true);
                        }
                  }
            } //}}}

            //{{{ Update this buffer for the new path
            if(rename)
            {
                  if(file != null)
                        modTime = file.lastModified();

                  if(!error)
                  {
                        // we do a write lock so that the
                        // autosave, which grabs a read lock,
                        // is not executed between the
                        // deletion of the autosave file
                        // and clearing of the dirty flag
                        try
                        {
                              writeLock();

                              if(autosaveFile != null)
                                    autosaveFile.delete();

                              setFlag(AUTOSAVE_DIRTY,false);
                              setFileReadOnly(false);
                              setFlag(NEW_FILE,false);
                              setFlag(UNTITLED,false);
                              super.setDirty(false);
                              if(jEdit.getBooleanProperty("resetUndoOnSave"))
                              {
                                    undoMgr.clear();
                              }
                        }
                        finally
                        {
                              writeUnlock();
                        }

                        parseBufferLocalProperties();

                        if(!getPath().equals(oldPath))
                        {
                              if (!isTemporary())
                                    jEdit.updatePosition(oldSymlinkPath,this);
                              setMode();
                        }
                        else
                        {
                              // if user adds mode buffer-local property
                              String newMode = getStringProperty("mode");
                              if(newMode != null &&
                                    !newMode.equals(getMode()
                                    .getName()))
                                    setMode();
                              else
                                    propertiesChanged();
                        }

                        updateHash();
                        
                        if (!isTemporary())
                        {
                              EditBus.send(new BufferUpdate(this,
                                                      view,BufferUpdate.DIRTY_CHANGED));

                              // new message type introduced in 4.0pre4
                              EditBus.send(new BufferUpdate(this,
                                                      view,BufferUpdate.SAVED));
                        }
                  }
            } //}}}
      } //}}}

      //{{{ editSyntaxStyle() method
      /**
       * Edit the syntax style of the token under the caret.
       *
       * @param textArea the textarea where your caret is
       * @since jEdit 4.3pre11
       */
02119       public void editSyntaxStyle(JEditTextArea textArea)
      {
            int lineNum = textArea.getCaretLine();
            int start = getLineStartOffset(lineNum);
            int position = textArea.getCaretPosition();

            DefaultTokenHandler tokenHandler = new DefaultTokenHandler();
            markTokens(lineNum,tokenHandler);
            Token token = tokenHandler.getTokens();

            while(token.id != Token.END)
            {
                  int next = start + token.length;
                  if (start <= position && next > position)
                        break;
                  start = next;
                  token = token.next;
            }
            if (token.id == Token.END || token.id == Token.NULL)
            {
                  JOptionPane.showMessageDialog(jEdit.getActiveView(),
                        jEdit.getProperty("syntax-style-no-token.message"),
                        jEdit.getProperty("syntax-style-no-token.title"),
                        JOptionPane.PLAIN_MESSAGE);
                  return;
            }
            String typeName = Token.tokenToString(token.id);
            String property = "view.style." + typeName.toLowerCase();
            SyntaxStyle currentStyle = GUIUtilities.parseStyle(
                        jEdit.getProperty(property), "Dialog",12);
            SyntaxStyle style = new StyleEditor(jEdit.getActiveView(),
                        currentStyle, typeName).getStyle();
            if(style != null)
            {
                  jEdit.setProperty(property, GUIUtilities.getStyleString(style));
                  jEdit.propertiesChanged();
            }
      } //}}}
      //}}}
}

Generated by  Doxygen 1.6.0   Back to index