PageRenderTime 61ms CodeModel.GetById 10ms app.highlight 43ms RepoModel.GetById 1ms app.codeStats 0ms

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

#
Java | 875 lines | 606 code | 108 blank | 161 comment | 102 complexity | 3bc4df5c4d243dcd5e5bea527bbffbfb 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 4761 2003-06-05 00:44:54Z 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	//}}}
 90
 91	//{{{ BufferIORequest constructor
 92	/**
 93	 * Creates a new buffer I/O request.
 94	 * @param type The request type
 95	 * @param view The view
 96	 * @param buffer The buffer
 97	 * @param session The VFS session
 98	 * @param vfs The VFS
 99	 * @param path The path
100	 */
101	public BufferIORequest(int type, View view, Buffer buffer,
102		Object session, VFS vfs, String path)
103	{
104		this.type = type;
105		this.view = view;
106		this.buffer = buffer;
107		this.session = session;
108		this.vfs = vfs;
109		this.path = path;
110
111		markersPath = vfs.getParentOfPath(path)
112			+ '.' + vfs.getFileName(path)
113			+ ".marks";
114	} //}}}
115
116	//{{{ run() method
117	public void run()
118	{
119		switch(type)
120		{
121		case LOAD:
122			load();
123			break;
124		case SAVE:
125			save();
126			break;
127		case AUTOSAVE:
128			autosave();
129			break;
130		case INSERT:
131			insert();
132			break;
133		}
134	} //}}}
135
136	//{{{ toString() method
137	public String toString()
138	{
139		String typeString;
140		switch(type)
141		{
142		case LOAD:
143			typeString = "LOAD";
144			break;
145		case SAVE:
146			typeString = "SAVE";
147			break;
148		case AUTOSAVE:
149			typeString = "AUTOSAVE";
150			break;
151		default:
152			typeString = "UNKNOWN!!!";
153		}
154
155		return getClass().getName() + "[type=" + typeString
156			+ ",buffer=" + buffer + "]";
157	} //}}}
158
159	//{{{ Private members
160
161	//{{{ Instance variables
162	private int type;
163	private View view;
164	private Buffer buffer;
165	private Object session;
166	private VFS vfs;
167	private String path;
168	private String markersPath;
169	//}}}
170
171	//{{{ load() method
172	private void load()
173	{
174		InputStream in = null;
175
176		try
177		{
178			try
179			{
180				String[] args = { vfs.getFileName(path) };
181				setAbortable(true);
182				if(!buffer.isTemporary())
183				{
184					setStatus(jEdit.getProperty("vfs.status.load",args));
185					setProgressValue(0);
186				}
187
188				path = vfs._canonPath(session,path,view);
189
190				VFS.DirectoryEntry entry = vfs._getDirectoryEntry(
191					session,path,view);
192				long length;
193				if(entry != null)
194					length = entry.length;
195				else
196					length = 0L;
197
198				in = vfs._createInputStream(session,path,false,view);
199				if(in == null)
200					return;
201
202				in = new BufferedInputStream(in);
203
204				if(in.markSupported())
205				{
206					in.mark(2);
207					int b1 = in.read();
208					int b2 = in.read();
209					in.reset();
210
211					if(b1 == GZIP_MAGIC_1 && b2 == GZIP_MAGIC_2)
212					{
213						in = new GZIPInputStream(in);
214						buffer.setBooleanProperty(Buffer.GZIPPED,true);
215					}
216					else if((b1 == UNICODE_MAGIC_1 && b2 == UNICODE_MAGIC_2)
217						|| (b1 == UNICODE_MAGIC_2 && b2 == UNICODE_MAGIC_1))
218					{
219						buffer.setProperty(Buffer.ENCODING,"Unicode");
220					}
221				}
222				else if(path.toLowerCase().endsWith(".gz"))
223					in = new GZIPInputStream(in);
224
225				read(buffer,in,length);
226				buffer.setNewFile(false);
227			}
228			catch(CharConversionException ch)
229			{
230				Log.log(Log.ERROR,this,ch);
231				Object[] pp = { buffer.getProperty(Buffer.ENCODING),
232					ch.toString() };
233				VFSManager.error(view,path,"ioerror.encoding-error",pp);
234
235				buffer.setBooleanProperty(ERROR_OCCURRED,true);
236			}
237			catch(UnsupportedEncodingException uu)
238			{
239				Log.log(Log.ERROR,this,uu);
240				Object[] pp = { buffer.getProperty(Buffer.ENCODING),
241					uu.toString() };
242				VFSManager.error(view,path,"ioerror.encoding-error",pp);
243
244				buffer.setBooleanProperty(ERROR_OCCURRED,true);
245			}
246			catch(IOException io)
247			{
248				Log.log(Log.ERROR,this,io);
249				Object[] pp = { io.toString() };
250				VFSManager.error(view,path,"ioerror.read-error",pp);
251
252				buffer.setBooleanProperty(ERROR_OCCURRED,true);
253			}
254			catch(OutOfMemoryError oom)
255			{
256				Log.log(Log.ERROR,this,oom);
257				VFSManager.error(view,path,"out-of-memory-error",null);
258
259				buffer.setBooleanProperty(ERROR_OCCURRED,true);
260			}
261
262			if(jEdit.getBooleanProperty("persistentMarkers"))
263			{
264				try
265				{
266					String[] args = { vfs.getFileName(path) };
267					if(!buffer.isTemporary())
268						setStatus(jEdit.getProperty("vfs.status.load-markers",args));
269					setAbortable(true);
270
271					in = vfs._createInputStream(session,markersPath,true,view);
272					if(in != null)
273						readMarkers(buffer,in);
274				}
275				catch(IOException io)
276				{
277					// ignore
278				}
279			}
280		}
281		catch(WorkThread.Abort a)
282		{
283			if(in != null)
284			{
285				try
286				{
287					in.close();
288				}
289				catch(IOException io)
290				{
291				}
292			}
293
294			buffer.setBooleanProperty(ERROR_OCCURRED,true);
295		}
296		finally
297		{
298			try
299			{
300				vfs._endVFSSession(session,view);
301			}
302			catch(IOException io)
303			{
304				Log.log(Log.ERROR,this,io);
305				String[] pp = { io.toString() };
306				VFSManager.error(view,path,"ioerror.read-error",pp);
307
308				buffer.setBooleanProperty(ERROR_OCCURRED,true);
309			}
310			catch(WorkThread.Abort a)
311			{
312				buffer.setBooleanProperty(ERROR_OCCURRED,true);
313			}
314		}
315	} //}}}
316
317	//{{{ read() method
318	private void read(Buffer buffer, InputStream _in, long length)
319		throws IOException
320	{
321		/* we guess an initial size for the array */
322		IntegerArray endOffsets = new IntegerArray(
323			Math.max(1,(int)(length / 50)));
324
325		// only true if the file size is known
326		boolean trackProgress = (!buffer.isTemporary() && length != 0);
327
328		if(trackProgress)
329		{
330			setProgressValue(0);
331			setProgressMaximum((int)length);
332		}
333
334		// if the file size is not known, start with a resonable
335		// default buffer size
336		if(length == 0)
337			length = IOBUFSIZE;
338
339		SegmentBuffer seg = new SegmentBuffer((int)length + 1);
340
341		InputStreamReader in = new InputStreamReader(_in,
342			buffer.getStringProperty(Buffer.ENCODING));
343		char[] buf = new char[IOBUFSIZE];
344
345		// Number of characters in 'buf' array.
346		// InputStream.read() doesn't always fill the
347		// array (eg, the file size is not a multiple of
348		// IOBUFSIZE, or it is a GZipped file, etc)
349		int len;
350
351		// True if a \n was read after a \r. Usually
352		// means this is a DOS/Windows file
353		boolean CRLF = false;
354
355		// A \r was read, hence a MacOS file
356		boolean CROnly = false;
357
358		// Was the previous read character a \r?
359		// If we read a \n and this is true, we assume
360		// we have a DOS/Windows file
361		boolean lastWasCR = false;
362
363		// Number of lines read. Every 100 lines, we update the
364		// progress bar
365		int lineCount = 0;
366
367		while((len = in.read(buf,0,buf.length)) != -1)
368		{
369			// Offset of previous line, relative to
370			// the start of the I/O buffer (NOT
371			// relative to the start of the document)
372			int lastLine = 0;
373
374			for(int i = 0; i < len; i++)
375			{
376				// Look for line endings.
377				switch(buf[i])
378				{
379				case '\r':
380					// If we read a \r and
381					// lastWasCR is also true,
382					// it is probably a Mac file
383					// (\r\r in stream)
384					if(lastWasCR)
385					{
386						CROnly = true;
387						CRLF = false;
388					}
389					// Otherwise set a flag,
390					// so that \n knows that last
391					// was a \r
392					else
393					{
394						lastWasCR = true;
395					}
396
397					// Insert a line
398					seg.append(buf,lastLine,i -
399						lastLine);
400					seg.append('\n');
401					endOffsets.add(seg.count);
402					if(trackProgress && lineCount++ % PROGRESS_INTERVAL == 0)
403						setProgressValue(seg.count);
404
405					// This is i+1 to take the
406					// trailing \n into account
407					lastLine = i + 1;
408					break;
409				case '\n':
410					// If lastWasCR is true,
411					// we just read a \r followed
412					// by a \n. We specify that
413					// this is a Windows file,
414					// but take no further
415					// action and just ignore
416					// the \r.
417					if(lastWasCR)
418					{
419						CROnly = false;
420						CRLF = true;
421						lastWasCR = false;
422						// Bump lastLine so
423						// that the next line
424						// doesn't erronously
425						// pick up the \r
426						lastLine = i + 1;
427					}
428					// Otherwise, we found a \n
429					// that follows some other
430					// character, hence we have
431					// a Unix file
432					else
433					{
434						CROnly = false;
435						CRLF = false;
436						seg.append(buf,lastLine,
437							i - lastLine);
438						seg.append('\n');
439						endOffsets.add(seg.count);
440						if(trackProgress && lineCount++ % PROGRESS_INTERVAL == 0)
441							setProgressValue(seg.count);
442						lastLine = i + 1;
443					}
444					break;
445				default:
446					// If we find some other
447					// character that follows
448					// a \r, so it is not a
449					// Windows file, and probably
450					// a Mac file
451					if(lastWasCR)
452					{
453						CROnly = true;
454						CRLF = false;
455						lastWasCR = false;
456					}
457					break;
458				}
459			}
460
461			if(trackProgress)
462				setProgressValue(seg.count);
463
464			// Add remaining stuff from buffer
465			seg.append(buf,lastLine,len - lastLine);
466		}
467
468		setAbortable(false);
469
470		String lineSeparator;
471		if(CRLF)
472			lineSeparator = "\r\n";
473		else if(CROnly)
474			lineSeparator = "\r";
475		else
476			lineSeparator = "\n";
477
478		in.close();
479
480		// Chop trailing newline and/or ^Z (if any)
481		int bufferLength = seg.count;
482		if(bufferLength != 0)
483		{
484			char ch = seg.array[bufferLength - 1];
485			if(ch == 0x1a /* DOS ^Z */)
486				seg.count--;
487		}
488
489		buffer.setBooleanProperty(Buffer.TRAILING_EOL,false);
490		if(bufferLength != 0 && jEdit.getBooleanProperty("stripTrailingEOL"))
491		{
492			char ch = seg.array[bufferLength - 1];
493			if(ch == '\n')
494			{
495				buffer.setBooleanProperty(Buffer.TRAILING_EOL,true);
496				seg.count--;
497				endOffsets.setSize(endOffsets.getSize() - 1);
498			}
499		}
500
501		// add a line marker at the end for proper offset manager
502		// operation
503		endOffsets.add(seg.count + 1);
504
505		// to avoid having to deal with read/write locks and such,
506		// we insert the loaded data into the buffer in the
507		// post-load cleanup runnable, which runs in the AWT thread.
508		buffer.setProperty(LOAD_DATA,seg);
509		buffer.setProperty(END_OFFSETS,endOffsets);
510		buffer.setProperty(NEW_PATH,path);
511		buffer.setProperty(Buffer.LINESEP,lineSeparator);
512	} //}}}
513
514	//{{{ readMarkers() method
515	private void readMarkers(Buffer buffer, InputStream _in)
516		throws IOException
517	{
518		// For `reload' command
519		buffer.removeAllMarkers();
520
521		BufferedReader in = new BufferedReader(new InputStreamReader(_in));
522
523		String line;
524		while((line = in.readLine()) != null)
525		{
526			// compatibility kludge for jEdit 3.1 and earlier
527			if(!line.startsWith("!"))
528				continue;
529
530			char shortcut = line.charAt(1);
531			int start = line.indexOf(';');
532			int end = line.indexOf(';',start + 1);
533			int position = Integer.parseInt(line.substring(start + 1,end));
534			buffer.addMarker(shortcut,position);
535		}
536
537		in.close();
538	} //}}}
539
540	//{{{ save() method
541	private void save()
542	{
543		OutputStream out = null;
544
545		try
546		{
547			String[] args = { vfs.getFileName(path) };
548			setStatus(jEdit.getProperty("vfs.status.save",args));
549
550			// the entire save operation can be aborted...
551			setAbortable(true);
552
553			path = vfs._canonPath(session,path,view);			if(!MiscUtilities.isURL(path))
554				path = MiscUtilities.resolveSymlinks(path);
555
556			// Only backup once per session
557			if(buffer.getProperty(Buffer.BACKED_UP) == null
558				|| jEdit.getBooleanProperty("backupEverySave"))
559			{
560				vfs._backup(session,path,view);
561				buffer.setBooleanProperty(Buffer.BACKED_UP,true);
562			}
563
564			/* if the VFS supports renaming files, we first
565			 * save to #<filename>#save#, then rename that
566			 * to <filename>, so that if the save fails,
567			 * data will not be lost.
568			 *
569			 * as of 4.1pre7 we now call vfs.getTwoStageSaveName()
570			 * instead of constructing the path directly
571			 * since some VFS's might not allow # in filenames.
572			 */
573			String savePath;
574
575			boolean twoStageSave = (vfs.getCapabilities() & VFS.RENAME_CAP) != 0
576				&& jEdit.getBooleanProperty("twoStageSave");
577			if(twoStageSave)
578				savePath = vfs.getTwoStageSaveName(path);
579			else
580				savePath = path;
581
582			out = vfs._createOutputStream(session,savePath,view);
583
584			try
585			{
586				// this must be after the stream is created or
587				// we deadlock with SSHTools.
588				buffer.readLock();
589				if(out != null)
590				{
591					// Can't use buffer.getName() here because
592					// it is not changed until the save is
593					// complete
594					if(savePath.endsWith(".gz"))
595						buffer.setBooleanProperty(Buffer.GZIPPED,true);
596
597					if(buffer.getBooleanProperty(Buffer.GZIPPED))
598						out = new GZIPOutputStream(out);
599
600					write(buffer,out);
601
602					if(twoStageSave)
603					{
604						if(!vfs._rename(session,savePath,path,view))
605							throw new IOException(savePath);
606					}
607
608					// We only save markers to VFS's that support deletion.
609					// Otherwise, we will accumilate stale marks files.
610					if((vfs.getCapabilities() & VFS.DELETE_CAP) != 0)
611					{
612						if(jEdit.getBooleanProperty("persistentMarkers")
613							&& buffer.getMarkers().size() != 0)
614						{
615							setStatus(jEdit.getProperty("vfs.status.save-markers",args));
616							setProgressValue(0);
617							out = vfs._createOutputStream(session,markersPath,view);
618							if(out != null)
619								writeMarkers(buffer,out);
620						}
621						else
622							vfs._delete(session,markersPath,view);
623					}
624				}
625				else
626					buffer.setBooleanProperty(ERROR_OCCURRED,true);
627
628				if(!twoStageSave)
629					VFSManager.sendVFSUpdate(vfs,path,true);
630			}
631			finally
632			{
633				buffer.readUnlock();
634			}
635		}
636		catch(IOException io)
637		{
638			Log.log(Log.ERROR,this,io);
639			String[] pp = { io.toString() };
640			VFSManager.error(view,path,"ioerror.write-error",pp);
641
642			buffer.setBooleanProperty(ERROR_OCCURRED,true);
643		}
644		catch(WorkThread.Abort a)
645		{
646			if(out != null)
647			{
648				try
649				{
650					out.close();
651				}
652				catch(IOException io)
653				{
654				}
655			}
656
657			buffer.setBooleanProperty(ERROR_OCCURRED,true);
658		}
659		finally
660		{
661			try
662			{
663				vfs._saveComplete(session,buffer,path,view);
664				vfs._endVFSSession(session,view);
665			}
666			catch(IOException io)
667			{
668				Log.log(Log.ERROR,this,io);
669				String[] pp = { io.toString() };
670				VFSManager.error(view,path,"ioerror.write-error",pp);
671
672				buffer.setBooleanProperty(ERROR_OCCURRED,true);
673			}
674			catch(WorkThread.Abort a)
675			{
676				buffer.setBooleanProperty(ERROR_OCCURRED,true);
677			}
678		}
679	} //}}}
680
681	//{{{ autosave() method
682	private void autosave()
683	{
684		OutputStream out = null;
685
686		try
687		{
688			String[] args = { vfs.getFileName(path) };
689			setStatus(jEdit.getProperty("vfs.status.autosave",args));
690
691			// the entire save operation can be aborted...
692			setAbortable(true);
693
694			try
695			{
696				//buffer.readLock();
697
698				if(!buffer.isDirty())
699				{
700					// buffer has been saved while we
701					// were waiting.
702					return;
703				}
704
705				out = vfs._createOutputStream(session,path,view);
706				if(out == null)
707					return;
708
709				write(buffer,out);
710			}
711			catch(Exception e)
712			{
713			}
714			finally
715			{
716				//buffer.readUnlock();
717			}
718		}
719		catch(WorkThread.Abort a)
720		{
721			if(out != null)
722			{
723				try
724				{
725					out.close();
726				}
727				catch(IOException io)
728				{
729				}
730			}
731		}
732	} //}}}
733
734	//{{{ write() method
735	private void write(Buffer buffer, OutputStream _out)
736		throws IOException
737	{
738		BufferedWriter out = new BufferedWriter(
739			new OutputStreamWriter(_out,
740				buffer.getStringProperty(Buffer.ENCODING)),
741				IOBUFSIZE);
742		Segment lineSegment = new Segment();
743		String newline = buffer.getStringProperty(Buffer.LINESEP);
744		if(newline == null)
745			newline = System.getProperty("line.separator");
746
747		setProgressMaximum(buffer.getLineCount() / PROGRESS_INTERVAL);
748		setProgressValue(0);
749
750		int i = 0;
751		while(i < buffer.getLineCount())
752		{
753			buffer.getLineText(i,lineSegment);
754			out.write(lineSegment.array,lineSegment.offset,
755				lineSegment.count);
756
757			if(i != buffer.getLineCount() - 1)
758			{
759				out.write(newline);
760			}
761
762			if(++i % PROGRESS_INTERVAL == 0)
763				setProgressValue(i / PROGRESS_INTERVAL);
764		}
765
766		if(jEdit.getBooleanProperty("stripTrailingEOL")
767			&& buffer.getBooleanProperty(Buffer.TRAILING_EOL))
768		{
769			out.write(newline);
770		}
771
772		out.close();
773	} //}}}
774
775	//{{{ writeMarkers() method
776	private void writeMarkers(Buffer buffer, OutputStream out)
777		throws IOException
778	{
779		Writer o = new BufferedWriter(new OutputStreamWriter(out));
780		Vector markers = buffer.getMarkers();
781		for(int i = 0; i < markers.size(); i++)
782		{
783			Marker marker = (Marker)markers.elementAt(i);
784			o.write('!');
785			o.write(marker.getShortcut());
786			o.write(';');
787
788			String pos = String.valueOf(marker.getPosition());
789			o.write(pos);
790			o.write(';');
791			o.write(pos);
792			o.write('\n');
793		}
794		o.close();
795	} //}}}
796
797	//{{{ insert() method
798	private void insert()
799	{
800		InputStream in = null;
801
802		try
803		{
804			try
805			{
806				String[] args = { vfs.getFileName(path) };
807				setStatus(jEdit.getProperty("vfs.status.load",args));
808				setAbortable(true);
809
810				path = vfs._canonPath(session,path,view);
811
812				VFS.DirectoryEntry entry = vfs._getDirectoryEntry(
813					session,path,view);
814				long length;
815				if(entry != null)
816					length = entry.length;
817				else
818					length = 0L;
819
820				in = vfs._createInputStream(session,path,false,view);
821				if(in == null)
822					return;
823
824				if(path.endsWith(".gz"))
825					in = new GZIPInputStream(in);
826
827				read(buffer,in,length);
828			}
829			catch(IOException io)
830			{
831				Log.log(Log.ERROR,this,io);
832				String[] pp = { io.toString() };
833				VFSManager.error(view,path,"ioerror.read-error",pp);
834
835				buffer.setBooleanProperty(ERROR_OCCURRED,true);
836			}
837		}
838		catch(WorkThread.Abort a)
839		{
840			if(in != null)
841			{
842				try
843				{
844					in.close();
845				}
846				catch(IOException io)
847				{
848				}
849			}
850
851			buffer.setBooleanProperty(ERROR_OCCURRED,true);
852		}
853		finally
854		{
855			try
856			{
857				vfs._endVFSSession(session,view);
858			}
859			catch(IOException io)
860			{
861				Log.log(Log.ERROR,this,io);
862				String[] pp = { io.toString() };
863				VFSManager.error(view,path,"ioerror.read-error",pp);
864
865				buffer.setBooleanProperty(ERROR_OCCURRED,true);
866			}
867			catch(WorkThread.Abort a)
868			{
869				buffer.setBooleanProperty(ERROR_OCCURRED,true);
870			}
871		}
872	} //}}}
873
874	//}}}
875}