PageRenderTime 235ms CodeModel.GetById 178ms app.highlight 48ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre14/org/gjt/sp/jedit/buffer/BufferIORequest.java

#
Java | 1031 lines | 727 code | 118 blank | 186 comment | 134 complexity | c451cf14d3e44826e518606fc5b0daf5 MD5 | raw file
   1/*
   2 * BufferIORequest.java - I/O request
   3 * :tabSize=8:indentSize=8:noTabs=false:
   4 * :folding=explicit:collapseFolds=1:
   5 *
   6 * Copyright (C) 2000, 2003 Slava Pestov
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License
  10 * as published by the Free Software Foundation; either version 2
  11 * of the License, or any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  21 */
  22
  23package org.gjt.sp.jedit.buffer;
  24
  25//{{{ Imports
  26import javax.swing.text.Segment;
  27import java.io.*;
  28import java.util.zip.*;
  29import java.util.Vector;
  30import org.gjt.sp.jedit.io.*;
  31import org.gjt.sp.jedit.*;
  32import org.gjt.sp.util.*;
  33//}}}
  34
  35/**
  36 * A buffer I/O request.
  37 * @author Slava Pestov
  38 * @version $Id: BufferIORequest.java 5053 2004-05-29 01:55:26Z spestov $
  39 */
  40public class BufferIORequest extends WorkRequest
  41{
  42	//{{{ Constants
  43	/**
  44	 * Size of I/O buffers.
  45	 */
  46	public static final int IOBUFSIZE = 32768;
  47
  48	/**
  49	 * Number of lines per progress increment.
  50	 */
  51	public static final int PROGRESS_INTERVAL = 300;
  52
  53	public static final String LOAD_DATA = "BufferIORequest__loadData";
  54	public static final String END_OFFSETS = "BufferIORequest__endOffsets";
  55	public static final String NEW_PATH = "BufferIORequest__newPath";
  56
  57	/**
  58	 * Buffer boolean property set when an error occurs.
  59	 */
  60	public static final String ERROR_OCCURRED = "BufferIORequest__error";
  61
  62	/**
  63	 * A file load request.
  64	 */
  65	public static final int LOAD = 0;
  66
  67	/**
  68	 * A file save request.
  69	 */
  70	public static final int SAVE = 1;
  71
  72	/**
  73	 * An autosave request. Only supported for local files.
  74	 */
  75	public static final int AUTOSAVE = 2;
  76
  77	/**
  78	 * An insert file request.
  79	 */
  80	public static final int INSERT = 3;
  81
  82	/**
  83	 * Magic numbers used for auto-detecting Unicode and GZIP files.
  84	 */
  85	public static final int GZIP_MAGIC_1 = 0x1f;
  86	public static final int GZIP_MAGIC_2 = 0x8b;
  87	public static final int UNICODE_MAGIC_1 = 0xfe;
  88	public static final int UNICODE_MAGIC_2 = 0xff;
  89	public static final int UTF8_MAGIC_1 = 0xef;
  90	public static final int UTF8_MAGIC_2 = 0xbb;
  91	public static final int UTF8_MAGIC_3 = 0xbf;
  92
  93	/**
  94	 * Length of longest XML PI used for encoding detection.<p>
  95	 * &lt;?xml version="1.0" encoding="................"?&gt;
  96	 */
  97	public static final int XML_PI_LENGTH = 50;
  98	//}}}
  99
 100	//{{{ BufferIORequest constructor
 101	/**
 102	 * Creates a new buffer I/O request.
 103	 * @param type The request type
 104	 * @param view The view
 105	 * @param buffer The buffer
 106	 * @param session The VFS session
 107	 * @param vfs The VFS
 108	 * @param path The path
 109	 */
 110	public BufferIORequest(int type, View view, Buffer buffer,
 111		Object session, VFS vfs, String path)
 112	{
 113		this.type = type;
 114		this.view = view;
 115		this.buffer = buffer;
 116		this.session = session;
 117		this.vfs = vfs;
 118		this.path = path;
 119
 120		markersPath = vfs.getParentOfPath(path)
 121			+ '.' + vfs.getFileName(path)
 122			+ ".marks";
 123	} //}}}
 124
 125	//{{{ run() method
 126	public void run()
 127	{
 128		switch(type)
 129		{
 130		case LOAD:
 131			load();
 132			break;
 133		case SAVE:
 134			save();
 135			break;
 136		case AUTOSAVE:
 137			autosave();
 138			break;
 139		case INSERT:
 140			insert();
 141			break;
 142		default:
 143			throw new InternalError();
 144		}
 145	} //}}}
 146
 147	//{{{ toString() method
 148	public String toString()
 149	{
 150		String typeString;
 151		switch(type)
 152		{
 153		case LOAD:
 154			typeString = "LOAD";
 155			break;
 156		case SAVE:
 157			typeString = "SAVE";
 158			break;
 159		case AUTOSAVE:
 160			typeString = "AUTOSAVE";
 161			break;
 162		default:
 163			typeString = "UNKNOWN!!!";
 164		}
 165
 166		return getClass().getName() + "[type=" + typeString
 167			+ ",buffer=" + buffer + "]";
 168	} //}}}
 169
 170	//{{{ Private members
 171
 172	//{{{ Instance variables
 173	private int type;
 174	private View view;
 175	private Buffer buffer;
 176	private Object session;
 177	private VFS vfs;
 178	private String path;
 179	private String markersPath;
 180	//}}}
 181
 182	//{{{ load() method
 183	private void load()
 184	{
 185		InputStream in = null;
 186
 187		try
 188		{
 189			try
 190			{
 191				String[] args = { vfs.getFileName(path) };
 192				setAbortable(true);
 193				if(!buffer.isTemporary())
 194				{
 195					setStatus(jEdit.getProperty("vfs.status.load",args));
 196					setProgressValue(0);
 197				}
 198
 199				path = vfs._canonPath(session,path,view);
 200
 201				VFS.DirectoryEntry entry = vfs._getDirectoryEntry(
 202					session,path,view);
 203				long length;
 204				if(entry != null)
 205					length = entry.length;
 206				else
 207					length = 0L;
 208
 209				in = vfs._createInputStream(session,path,
 210					false,view);
 211				if(in == null)
 212					return;
 213
 214				read(autodetect(in),length,false);
 215				buffer.setNewFile(false);
 216			}
 217			catch(CharConversionException ch)
 218			{
 219				Log.log(Log.ERROR,this,ch);
 220				Object[] pp = { buffer.getProperty(Buffer.ENCODING),
 221					ch.toString() };
 222				VFSManager.error(view,path,"ioerror.encoding-error",pp);
 223
 224				buffer.setBooleanProperty(ERROR_OCCURRED,true);
 225			}
 226			catch(UnsupportedEncodingException uu)
 227			{
 228				Log.log(Log.ERROR,this,uu);
 229				Object[] pp = { buffer.getProperty(Buffer.ENCODING),
 230					uu.toString() };
 231				VFSManager.error(view,path,"ioerror.encoding-error",pp);
 232
 233				buffer.setBooleanProperty(ERROR_OCCURRED,true);
 234			}
 235			catch(IOException io)
 236			{
 237				Log.log(Log.ERROR,this,io);
 238				Object[] pp = { io.toString() };
 239				VFSManager.error(view,path,"ioerror.read-error",pp);
 240
 241				buffer.setBooleanProperty(ERROR_OCCURRED,true);
 242			}
 243			catch(OutOfMemoryError oom)
 244			{
 245				Log.log(Log.ERROR,this,oom);
 246				VFSManager.error(view,path,"out-of-memory-error",null);
 247
 248				buffer.setBooleanProperty(ERROR_OCCURRED,true);
 249			}
 250
 251			if(jEdit.getBooleanProperty("persistentMarkers"))
 252			{
 253				try
 254				{
 255					String[] args = { vfs.getFileName(path) };
 256					if(!buffer.isTemporary())
 257						setStatus(jEdit.getProperty("vfs.status.load-markers",args));
 258					setAbortable(true);
 259
 260					in = vfs._createInputStream(session,markersPath,true,view);
 261					if(in != null)
 262						readMarkers(buffer,in);
 263				}
 264				catch(IOException io)
 265				{
 266					// ignore
 267				}
 268			}
 269		}
 270		catch(WorkThread.Abort a)
 271		{
 272			if(in != null)
 273			{
 274				try
 275				{
 276					in.close();
 277				}
 278				catch(IOException io)
 279				{
 280				}
 281			}
 282
 283			buffer.setBooleanProperty(ERROR_OCCURRED,true);
 284		}
 285		finally
 286		{
 287			try
 288			{
 289				vfs._endVFSSession(session,view);
 290			}
 291			catch(IOException io)
 292			{
 293				Log.log(Log.ERROR,this,io);
 294				String[] pp = { io.toString() };
 295				VFSManager.error(view,path,"ioerror.read-error",pp);
 296
 297				buffer.setBooleanProperty(ERROR_OCCURRED,true);
 298			}
 299			catch(WorkThread.Abort a)
 300			{
 301				buffer.setBooleanProperty(ERROR_OCCURRED,true);
 302			}
 303		}
 304	} //}}}
 305
 306	//{{{ autodetect() method
 307	/**
 308	 * Tries to detect if the stream is gzipped, and if it has an encoding
 309	 * specified with an XML PI.
 310	 */
 311	private Reader autodetect(InputStream in) throws IOException
 312	{
 313		in = new BufferedInputStream(in);
 314
 315		String encoding = buffer.getStringProperty(Buffer.ENCODING);
 316		if(!in.markSupported())
 317			Log.log(Log.WARNING,this,"Mark not supported: " + in);
 318		else if(buffer.getBooleanProperty(Buffer.ENCODING_AUTODETECT))
 319		{
 320			in.mark(XML_PI_LENGTH);
 321			int b1 = in.read();
 322			int b2 = in.read();
 323			int b3 = in.read();
 324
 325			if(encoding.equals(MiscUtilities.UTF_8_Y))
 326			{
 327				// Java does not support this encoding so
 328				// we have to handle it manually.
 329				if(b1 != UTF8_MAGIC_1 || b2 != UTF8_MAGIC_2
 330					|| b3 != UTF8_MAGIC_3)
 331				{
 332					// file does not begin with UTF-8-Y
 333					// signature. reset stream, read as
 334					// UTF-8.
 335					in.reset();
 336				}
 337				else
 338				{
 339					// file begins with UTF-8-Y signature.
 340					// discard the signature, and read
 341					// the remainder as UTF-8.
 342				}
 343
 344				encoding = "UTF-8";
 345			}
 346			else if(b1 == GZIP_MAGIC_1 && b2 == GZIP_MAGIC_2)
 347			{
 348				in.reset();
 349				in = new GZIPInputStream(in);
 350				buffer.setBooleanProperty(Buffer.GZIPPED,true);
 351			}
 352			else if((b1 == UNICODE_MAGIC_1
 353				&& b2 == UNICODE_MAGIC_2)
 354				|| (b1 == UNICODE_MAGIC_2
 355				&& b2 == UNICODE_MAGIC_1))
 356			{
 357				in.reset();
 358				encoding = "UTF-16";
 359				buffer.setProperty(Buffer.ENCODING,encoding);
 360			}
 361			else if(b1 == UTF8_MAGIC_1 && b2 == UTF8_MAGIC_2
 362				&& b3 == UTF8_MAGIC_3)
 363			{
 364				// do not reset the stream and just treat it
 365				// like a normal UTF-8 file.
 366				buffer.setProperty(Buffer.ENCODING,
 367					MiscUtilities.UTF_8_Y);
 368
 369				encoding = "UTF-8";
 370			}
 371			else
 372			{
 373				in.reset();
 374
 375				byte[] _xmlPI = new byte[XML_PI_LENGTH];
 376				int offset = 0;
 377				int count;
 378				while((count = in.read(_xmlPI,offset,
 379					XML_PI_LENGTH - offset)) != -1)
 380				{
 381					offset += count;
 382					if(offset == XML_PI_LENGTH)
 383						break;
 384				}
 385
 386				String xmlPI = new String(_xmlPI,0,offset,
 387				"ASCII");
 388				if(xmlPI.startsWith("<?xml"))
 389				{
 390					int index = xmlPI.indexOf("encoding=");
 391					if(index != -1
 392						&& index + 9 != xmlPI.length())
 393					{
 394						char ch = xmlPI.charAt(index
 395						+ 9);
 396						int endIndex = xmlPI.indexOf(ch,
 397							index + 10);
 398						encoding = xmlPI.substring(
 399							index + 10,endIndex);
 400	
 401						if(MiscUtilities.isSupportedEncoding(encoding))
 402						{
 403							buffer.setProperty(Buffer.ENCODING,encoding);
 404						}
 405						else
 406						{
 407							Log.log(Log.WARNING,this,"XML PI specifies unsupported encoding: " + encoding);
 408						}
 409					}
 410				}
 411
 412				in.reset();
 413			}
 414		}
 415
 416		return new InputStreamReader(in,encoding);
 417	} //}}}
 418
 419	//{{{ read() method
 420	private SegmentBuffer read(Reader in, long length,
 421		boolean insert) throws IOException
 422	{
 423		/* we guess an initial size for the array */
 424		IntegerArray endOffsets = new IntegerArray(
 425			Math.max(1,(int)(length / 50)));
 426
 427		// only true if the file size is known
 428		boolean trackProgress = (!buffer.isTemporary() && length != 0);
 429
 430		if(trackProgress)
 431		{
 432			setProgressValue(0);
 433			setProgressMaximum((int)length);
 434		}
 435
 436		// if the file size is not known, start with a resonable
 437		// default buffer size
 438		if(length == 0)
 439			length = IOBUFSIZE;
 440
 441		SegmentBuffer seg = new SegmentBuffer((int)length + 1);
 442
 443		char[] buf = new char[IOBUFSIZE];
 444
 445		// Number of characters in 'buf' array.
 446		// InputStream.read() doesn't always fill the
 447		// array (eg, the file size is not a multiple of
 448		// IOBUFSIZE, or it is a GZipped file, etc)
 449		int len;
 450
 451		// True if a \n was read after a \r. Usually
 452		// means this is a DOS/Windows file
 453		boolean CRLF = false;
 454
 455		// A \r was read, hence a MacOS file
 456		boolean CROnly = false;
 457
 458		// Was the previous read character a \r?
 459		// If we read a \n and this is true, we assume
 460		// we have a DOS/Windows file
 461		boolean lastWasCR = false;
 462
 463		// Number of lines read. Every 100 lines, we update the
 464		// progress bar
 465		int lineCount = 0;
 466
 467		while((len = in.read(buf,0,buf.length)) != -1)
 468		{
 469			// Offset of previous line, relative to
 470			// the start of the I/O buffer (NOT
 471			// relative to the start of the document)
 472			int lastLine = 0;
 473
 474			for(int i = 0; i < len; i++)
 475			{
 476				// Look for line endings.
 477				switch(buf[i])
 478				{
 479				case '\r':
 480					// If we read a \r and
 481					// lastWasCR is also true,
 482					// it is probably a Mac file
 483					// (\r\r in stream)
 484					if(lastWasCR)
 485					{
 486						CROnly = true;
 487						CRLF = false;
 488					}
 489					// Otherwise set a flag,
 490					// so that \n knows that last
 491					// was a \r
 492					else
 493					{
 494						lastWasCR = true;
 495					}
 496
 497					// Insert a line
 498					seg.append(buf,lastLine,i -
 499						lastLine);
 500					seg.append('\n');
 501					endOffsets.add(seg.count);
 502					if(trackProgress && lineCount++ % PROGRESS_INTERVAL == 0)
 503						setProgressValue(seg.count);
 504
 505					// This is i+1 to take the
 506					// trailing \n into account
 507					lastLine = i + 1;
 508					break;
 509				case '\n':
 510					// If lastWasCR is true,
 511					// we just read a \r followed
 512					// by a \n. We specify that
 513					// this is a Windows file,
 514					// but take no further
 515					// action and just ignore
 516					// the \r.
 517					if(lastWasCR)
 518					{
 519						CROnly = false;
 520						CRLF = true;
 521						lastWasCR = false;
 522						// Bump lastLine so
 523						// that the next line
 524						// doesn't erronously
 525						// pick up the \r
 526						lastLine = i + 1;
 527					}
 528					// Otherwise, we found a \n
 529					// that follows some other
 530					// character, hence we have
 531					// a Unix file
 532					else
 533					{
 534						CROnly = false;
 535						CRLF = false;
 536						seg.append(buf,lastLine,
 537							i - lastLine);
 538						seg.append('\n');
 539						endOffsets.add(seg.count);
 540						if(trackProgress && lineCount++ % PROGRESS_INTERVAL == 0)
 541							setProgressValue(seg.count);
 542						lastLine = i + 1;
 543					}
 544					break;
 545				default:
 546					// If we find some other
 547					// character that follows
 548					// a \r, so it is not a
 549					// Windows file, and probably
 550					// a Mac file
 551					if(lastWasCR)
 552					{
 553						CROnly = true;
 554						CRLF = false;
 555						lastWasCR = false;
 556					}
 557					break;
 558				}
 559			}
 560
 561			if(trackProgress)
 562				setProgressValue(seg.count);
 563
 564			// Add remaining stuff from buffer
 565			seg.append(buf,lastLine,len - lastLine);
 566		}
 567
 568		setAbortable(false);
 569
 570		String lineSeparator;
 571		if(seg.count == 0)
 572		{
 573			// fix for "[ 865589 ] 0-byte files should open using
 574			// the default line seperator"
 575			lineSeparator = jEdit.getProperty(
 576				"buffer.lineSeparator",
 577				System.getProperty("line.separator"));
 578		}
 579		else if(CRLF)
 580			lineSeparator = "\r\n";
 581		else if(CROnly)
 582			lineSeparator = "\r";
 583		else
 584			lineSeparator = "\n";
 585
 586		in.close();
 587
 588		// Chop trailing newline and/or ^Z (if any)
 589		int bufferLength = seg.count;
 590		if(bufferLength != 0)
 591		{
 592			char ch = seg.array[bufferLength - 1];
 593			if(ch == 0x1a /* DOS ^Z */)
 594				seg.count--;
 595		}
 596
 597		buffer.setBooleanProperty(Buffer.TRAILING_EOL,false);
 598		if(bufferLength != 0 && jEdit.getBooleanProperty("stripTrailingEOL"))
 599		{
 600			char ch = seg.array[bufferLength - 1];
 601			if(ch == '\n')
 602			{
 603				buffer.setBooleanProperty(Buffer.TRAILING_EOL,true);
 604				seg.count--;
 605				endOffsets.setSize(endOffsets.getSize() - 1);
 606			}
 607		}
 608
 609		// add a line marker at the end for proper offset manager
 610		// operation
 611		endOffsets.add(seg.count + 1);
 612
 613		// to avoid having to deal with read/write locks and such,
 614		// we insert the loaded data into the buffer in the
 615		// post-load cleanup runnable, which runs in the AWT thread.
 616		if(!insert)
 617		{
 618			buffer.setProperty(LOAD_DATA,seg);
 619			buffer.setProperty(END_OFFSETS,endOffsets);
 620			buffer.setProperty(NEW_PATH,path);
 621			if(lineSeparator != null)
 622				buffer.setProperty(Buffer.LINESEP,lineSeparator);
 623		}
 624
 625		// used in insert()
 626		return seg;
 627	} //}}}
 628
 629	//{{{ readMarkers() method
 630	private void readMarkers(Buffer buffer, InputStream _in)
 631		throws IOException
 632	{
 633		// For `reload' command
 634		buffer.removeAllMarkers();
 635
 636		BufferedReader in = new BufferedReader(new InputStreamReader(_in));
 637
 638		try
 639		{
 640			String line;
 641			while((line = in.readLine()) != null)
 642			{
 643				// compatibility kludge for jEdit 3.1 and earlier
 644				if(!line.startsWith("!"))
 645					continue;
 646
 647				char shortcut = line.charAt(1);
 648				int start = line.indexOf(';');
 649				int end = line.indexOf(';',start + 1);
 650				int position = Integer.parseInt(line.substring(start + 1,end));
 651				buffer.addMarker(shortcut,position);
 652			}
 653		}
 654		finally
 655		{
 656			in.close();
 657		}
 658	} //}}}
 659
 660	//{{{ save() method
 661	private void save()
 662	{
 663		OutputStream out = null;
 664
 665		try
 666		{
 667			String[] args = { vfs.getFileName(path) };
 668			setStatus(jEdit.getProperty("vfs.status.save",args));
 669
 670			// the entire save operation can be aborted...
 671			setAbortable(true);
 672
 673			path = vfs._canonPath(session,path,view);			if(!MiscUtilities.isURL(path))
 674				path = MiscUtilities.resolveSymlinks(path);
 675
 676			// Only backup once per session
 677			if(buffer.getProperty(Buffer.BACKED_UP) == null
 678				|| jEdit.getBooleanProperty("backupEverySave"))
 679			{
 680				vfs._backup(session,path,view);
 681				buffer.setBooleanProperty(Buffer.BACKED_UP,true);
 682			}
 683
 684			/* if the VFS supports renaming files, we first
 685			 * save to #<filename>#save#, then rename that
 686			 * to <filename>, so that if the save fails,
 687			 * data will not be lost.
 688			 *
 689			 * as of 4.1pre7 we now call vfs.getTwoStageSaveName()
 690			 * instead of constructing the path directly
 691			 * since some VFS's might not allow # in filenames.
 692			 */
 693			String savePath;
 694
 695			boolean twoStageSave = (vfs.getCapabilities() & VFS.RENAME_CAP) != 0
 696				&& jEdit.getBooleanProperty("twoStageSave");
 697			if(twoStageSave)
 698				savePath = vfs.getTwoStageSaveName(path);
 699			else
 700				savePath = path;
 701
 702			out = vfs._createOutputStream(session,savePath,view);
 703
 704			try
 705			{
 706				// this must be after the stream is created or
 707				// we deadlock with SSHTools.
 708				buffer.readLock();
 709				if(out != null)
 710				{
 711					// Can't use buffer.getName() here because
 712					// it is not changed until the save is
 713					// complete
 714					if(savePath.endsWith(".gz"))
 715						buffer.setBooleanProperty(Buffer.GZIPPED,true);
 716
 717					if(buffer.getBooleanProperty(Buffer.GZIPPED))
 718						out = new GZIPOutputStream(out);
 719
 720					write(buffer,out);
 721
 722					if(twoStageSave)
 723					{
 724						if(!vfs._rename(session,savePath,path,view))
 725							throw new IOException("Rename failed: " + savePath);
 726					}
 727
 728					// We only save markers to VFS's that support deletion.
 729					// Otherwise, we will accumilate stale marks files.
 730					if((vfs.getCapabilities() & VFS.DELETE_CAP) != 0)
 731					{
 732						if(jEdit.getBooleanProperty("persistentMarkers")
 733							&& buffer.getMarkers().size() != 0)
 734						{
 735							setStatus(jEdit.getProperty("vfs.status.save-markers",args));
 736							setProgressValue(0);
 737							out = vfs._createOutputStream(session,markersPath,view);
 738							if(out != null)
 739								writeMarkers(buffer,out);
 740						}
 741						else
 742							vfs._delete(session,markersPath,view);
 743					}
 744				}
 745				else
 746					buffer.setBooleanProperty(ERROR_OCCURRED,true);
 747
 748				if(!twoStageSave)
 749					VFSManager.sendVFSUpdate(vfs,path,true);
 750			}
 751			finally
 752			{
 753				buffer.readUnlock();
 754			}
 755		}
 756		catch(IOException io)
 757		{
 758			Log.log(Log.ERROR,this,io);
 759			String[] pp = { io.toString() };
 760			VFSManager.error(view,path,"ioerror.write-error",pp);
 761
 762			buffer.setBooleanProperty(ERROR_OCCURRED,true);
 763		}
 764		catch(WorkThread.Abort a)
 765		{
 766			if(out != null)
 767			{
 768				try
 769				{
 770					out.close();
 771				}
 772				catch(IOException io)
 773				{
 774				}
 775			}
 776
 777			buffer.setBooleanProperty(ERROR_OCCURRED,true);
 778		}
 779		finally
 780		{
 781			try
 782			{
 783				vfs._saveComplete(session,buffer,path,view);
 784				vfs._endVFSSession(session,view);
 785			}
 786			catch(IOException io)
 787			{
 788				Log.log(Log.ERROR,this,io);
 789				String[] pp = { io.toString() };
 790				VFSManager.error(view,path,"ioerror.write-error",pp);
 791
 792				buffer.setBooleanProperty(ERROR_OCCURRED,true);
 793			}
 794			catch(WorkThread.Abort a)
 795			{
 796				buffer.setBooleanProperty(ERROR_OCCURRED,true);
 797			}
 798		}
 799	} //}}}
 800
 801	//{{{ autosave() method
 802	private void autosave()
 803	{
 804		OutputStream out = null;
 805
 806		try
 807		{
 808			String[] args = { vfs.getFileName(path) };
 809			setStatus(jEdit.getProperty("vfs.status.autosave",args));
 810
 811			// the entire save operation can be aborted...
 812			setAbortable(true);
 813
 814			try
 815			{
 816				//buffer.readLock();
 817
 818				if(!buffer.isDirty())
 819				{
 820					// buffer has been saved while we
 821					// were waiting.
 822					return;
 823				}
 824
 825				out = vfs._createOutputStream(session,path,view);
 826				if(out == null)
 827					return;
 828
 829				write(buffer,out);
 830			}
 831			catch(Exception e)
 832			{
 833			}
 834			finally
 835			{
 836				//buffer.readUnlock();
 837			}
 838		}
 839		catch(WorkThread.Abort a)
 840		{
 841			if(out != null)
 842			{
 843				try
 844				{
 845					out.close();
 846				}
 847				catch(IOException io)
 848				{
 849				}
 850			}
 851		}
 852	} //}}}
 853
 854	//{{{ write() method
 855	private void write(Buffer buffer, OutputStream _out)
 856		throws IOException
 857	{
 858		BufferedWriter out = null;
 859
 860		try
 861		{
 862			String encoding = buffer.getStringProperty(Buffer.ENCODING);
 863			if(encoding.equals(MiscUtilities.UTF_8_Y))
 864			{
 865				// not supported by Java...
 866				_out.write(UTF8_MAGIC_1);
 867				_out.write(UTF8_MAGIC_2);
 868				_out.write(UTF8_MAGIC_3);
 869				_out.flush();
 870				encoding = "UTF-8";
 871			}
 872
 873			out = new BufferedWriter(
 874				new OutputStreamWriter(_out,encoding),
 875				IOBUFSIZE);
 876
 877			Segment lineSegment = new Segment();
 878			String newline = buffer.getStringProperty(Buffer.LINESEP);
 879			if(newline == null)
 880				newline = System.getProperty("line.separator");
 881
 882			setProgressMaximum(buffer.getLineCount() / PROGRESS_INTERVAL);
 883			setProgressValue(0);
 884
 885			int i = 0;
 886			while(i < buffer.getLineCount())
 887			{
 888				buffer.getLineText(i,lineSegment);
 889				out.write(lineSegment.array,lineSegment.offset,
 890					lineSegment.count);
 891
 892				if(i != buffer.getLineCount() - 1)
 893				{
 894					out.write(newline);
 895				}
 896
 897				if(++i % PROGRESS_INTERVAL == 0)
 898					setProgressValue(i / PROGRESS_INTERVAL);
 899			}
 900
 901			if(jEdit.getBooleanProperty("stripTrailingEOL")
 902				&& buffer.getBooleanProperty(Buffer.TRAILING_EOL))
 903			{
 904				out.write(newline);
 905			}
 906		}
 907		finally
 908		{
 909			if(out != null)
 910				out.close();
 911			else
 912				_out.close();
 913		}
 914	} //}}}
 915
 916	//{{{ writeMarkers() method
 917	private void writeMarkers(Buffer buffer, OutputStream out)
 918		throws IOException
 919	{
 920		Writer o = new BufferedWriter(new OutputStreamWriter(out));
 921		try
 922		{
 923			Vector markers = buffer.getMarkers();
 924			for(int i = 0; i < markers.size(); i++)
 925			{
 926				Marker marker = (Marker)markers.elementAt(i);
 927				o.write('!');
 928				o.write(marker.getShortcut());
 929				o.write(';');
 930
 931				String pos = String.valueOf(marker.getPosition());
 932				o.write(pos);
 933				o.write(';');
 934				o.write(pos);
 935				o.write('\n');
 936			}
 937		}
 938		finally
 939		{
 940			o.close();
 941		}
 942	} //}}}
 943
 944	//{{{ insert() method
 945	private void insert()
 946	{
 947		InputStream in = null;
 948
 949		try
 950		{
 951			try
 952			{
 953				String[] args = { vfs.getFileName(path) };
 954				setStatus(jEdit.getProperty("vfs.status.load",args));
 955				setAbortable(true);
 956
 957				path = vfs._canonPath(session,path,view);
 958
 959				VFS.DirectoryEntry entry = vfs._getDirectoryEntry(
 960					session,path,view);
 961				long length;
 962				if(entry != null)
 963					length = entry.length;
 964				else
 965					length = 0L;
 966
 967				in = vfs._createInputStream(session,path,false,view);
 968				if(in == null)
 969					return;
 970
 971				final SegmentBuffer seg = read(
 972					autodetect(in),length,true);
 973
 974				/* we don't do this in Buffer.insert() so that
 975				   we can insert multiple files at once */
 976				VFSManager.runInAWTThread(new Runnable()
 977				{
 978					public void run()
 979					{
 980						view.getTextArea().setSelectedText(
 981							seg.toString());
 982					}
 983				});
 984			}
 985			catch(IOException io)
 986			{
 987				Log.log(Log.ERROR,this,io);
 988				String[] pp = { io.toString() };
 989				VFSManager.error(view,path,"ioerror.read-error",pp);
 990
 991				buffer.setBooleanProperty(ERROR_OCCURRED,true);
 992			}
 993		}
 994		catch(WorkThread.Abort a)
 995		{
 996			if(in != null)
 997			{
 998				try
 999				{
1000					in.close();
1001				}
1002				catch(IOException io)
1003				{
1004				}
1005			}
1006
1007			buffer.setBooleanProperty(ERROR_OCCURRED,true);
1008		}
1009		finally
1010		{
1011			try
1012			{
1013				vfs._endVFSSession(session,view);
1014			}
1015			catch(IOException io)
1016			{
1017				Log.log(Log.ERROR,this,io);
1018				String[] pp = { io.toString() };
1019				VFSManager.error(view,path,"ioerror.read-error",pp);
1020
1021				buffer.setBooleanProperty(ERROR_OCCURRED,true);
1022			}
1023			catch(WorkThread.Abort a)
1024			{
1025				buffer.setBooleanProperty(ERROR_OCCURRED,true);
1026			}
1027		}
1028	} //}}}
1029
1030	//}}}
1031}