KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > armedbear > j > Buffer


1 /*
2  * Buffer.java
3  *
4  * Copyright (C) 1998-2003 Peter Graves
5  * $Id: Buffer.java,v 1.52 2004/08/30 17:13:48 piso Exp $
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */

21
22 package org.armedbear.j;
23
24 import java.awt.Cursor JavaDoc;
25 import java.io.BufferedInputStream JavaDoc;
26 import java.io.BufferedOutputStream JavaDoc;
27 import java.io.BufferedReader JavaDoc;
28 import java.io.IOException JavaDoc;
29 import java.io.InputStream JavaDoc;
30 import java.io.InputStreamReader JavaDoc;
31 import java.io.OutputStream JavaDoc;
32 import java.lang.ref.SoftReference JavaDoc;
33 import java.util.ArrayList JavaDoc;
34 import java.util.Iterator JavaDoc;
35 import java.util.List JavaDoc;
36 import java.util.zip.GZIPInputStream JavaDoc;
37 import java.util.zip.GZIPOutputStream JavaDoc;
38 import java.util.zip.ZipEntry JavaDoc;
39 import javax.swing.Icon JavaDoc;
40 import javax.swing.SwingUtilities JavaDoc;
41 import javax.swing.undo.CannotRedoException JavaDoc;
42 import javax.swing.undo.CannotUndoException JavaDoc;
43 import javax.swing.undo.CompoundEdit JavaDoc;
44 import javax.swing.undo.UndoableEdit JavaDoc;
45
46 public class Buffer extends SystemBuffer
47 {
48     private static int untitledCount;
49
50     protected boolean isUntitled;
51
52     protected Formatter formatter;
53
54     protected String JavaDoc title;
55
56     private boolean needsParsing;
57
58     boolean needsRenumbering;
59
60     public final boolean needsRenumbering()
61     {
62         return needsRenumbering;
63     }
64
65     public final void needsRenumbering(boolean b)
66     {
67         needsRenumbering = b;
68     }
69
70     private int visibleLineCount;
71
72     protected boolean supportsUndo = true;
73
74     public final boolean supportsUndo()
75     {
76         return supportsUndo;
77     }
78
79     private int modCount;
80     private int saveModCount; // Value of modCount when last saved.
81

82     // Autosave.
83
protected boolean autosaveEnabled;
84     private File autosaveFile;
85     private int autosaveModCount; // Value of modCount when last autosaved.
86

87     private File cache;
88     private String JavaDoc listing;
89     public final String JavaDoc getListing()
90     {
91         return listing;
92     }
93     public final void setListing(String JavaDoc s)
94     {
95         this.listing = s;
96     }
97
98     private View lastView;
99
100     private boolean backedUp = false; // Ignored for local buffers.
101

102     private int fileType = FILETYPE_UNKNOWN;
103
104     private Compression compression;
105
106     public final Compression getCompression()
107     {
108         return compression;
109     }
110
111     public final void setCompression(Compression compression)
112     {
113         this.compression = compression;
114     }
115
116     protected PropertyList properties = new PropertyList();
117
118     private BackgroundProcess backgroundProcess;
119
120     private Mutex mutex = new Mutex();
121     private final ReadWriteLock rwlock = new ReadWriteLock();
122
123     private boolean isNewFile;
124
125     protected Buffer parentBuffer;
126
127     public final Buffer getParentBuffer()
128     {
129         return parentBuffer;
130     }
131
132     public final void setParentBuffer(Buffer b)
133     {
134         this.parentBuffer = b;
135     }
136
137     public boolean isPrimary()
138     {
139         return true;
140     }
141
142     public boolean isSecondary()
143     {
144         return false;
145     }
146
147     public boolean isPaired()
148     {
149         return isSecondary() || getSecondary() != null;
150     }
151
152     public Buffer getPrimary()
153     {
154         return null;
155     }
156
157     public Buffer getSecondary()
158     {
159         return null;
160     }
161
162     public float getSplit()
163     {
164         return 0.5F;
165     }
166
167     public void promote()
168     {
169     }
170
171     public final boolean isNewFile()
172     {
173         return isNewFile;
174     }
175
176     private final void setNewFile(boolean b)
177     {
178         isNewFile = b;
179     }
180
181     protected Buffer()
182     {
183         // Add new buffer to global buffer list.
184
Editor.getBufferList().add(this);
185     }
186
187     // Called only by Editor.newBuffer().
188
public Buffer(int i /*ignored*/)
189     {
190         this();
191         Debug.assertTrue(Editor.getBufferList().contains(this));
192         initializeUndo();
193         type = TYPE_NORMAL;
194         isUntitled = true;
195         ++untitledCount;
196         String JavaDoc name = "Untitled-" + untitledCount;
197         File directory = Editor.currentEditor().getCurrentDirectory();
198         if (directory == null || directory.isRemote())
199             directory = Directories.getUserHomeDirectory();
200         setFile(File.getInstance(directory, name));
201         autosaveEnabled = true;
202         lineSeparator = System.getProperty("line.separator");
203         mode = PlainTextMode.getMode();
204         formatter = mode.getFormatter(this);
205         setNewFile(true);
206         try {
207             lockWrite();
208         }
209         catch (InterruptedException JavaDoc e) {
210             Log.debug(e);
211             return; // Shouldn't happen.
212
}
213         try {
214             appendLine("");
215             renumber();
216             setLoaded(true);
217         }
218         finally {
219             unlockWrite();
220         }
221     }
222
223     public Buffer(File file)
224     {
225         this();
226         Debug.assertTrue(Editor.getBufferList().contains(this));
227         initializeUndo();
228         setFile(file);
229         type = TYPE_NORMAL;
230         autosaveEnabled = true;
231     }
232
233     public static Buffer createBuffer(File file)
234     {
235         if (file instanceof FtpFile) {
236             FtpSession session = FtpSession.getSession((FtpFile)file);
237             if (session == null)
238                 return null;
239             return new RemoteBuffer((FtpFile)file, session);
240         }
241         if (file instanceof HttpFile) {
242             if (Editor.getModeList().modeAccepts(IMAGE_MODE, file.getName()))
243                 return new RemoteBuffer(file);
244             if (Editor.preferences().getBooleanProperty(Property.ENABLE_WEB)) {
245                 int modeId =
246                     Editor.getModeList().getModeIdForFileName(file.getName());
247                 if (modeId < 0 || modeId == HTML_MODE)
248                     return WebBuffer.createWebBuffer(file, null, null);
249             }
250             return new RemoteBuffer(file);
251         }
252         if (file instanceof SshFile) {
253             SshFile sshFile = (SshFile) file;
254             SshSession session = SshSession.getSession(sshFile);
255             if (session == null)
256                 return null;
257             if (!session.isLocked()) {
258                 Debug.bug();
259                 return null;
260             }
261             session.checkLogin();
262             if (sshFile.getUserName() == null) {
263                 Debug.bug();
264                 sshFile.setUserName(session.getUserName());
265             }
266             if (!sshFile.getUserName().equals(session.getUserName())) {
267                 Debug.bug();
268                 session.unlock();
269                 return null;
270             }
271             session.unlock();
272             return new RemoteBuffer(file);
273         }
274         // Special case for unsent messages.
275
File dir = file.getParentFile();
276         if (dir != null && dir.equals(Directories.getDraftsFolder())) {
277             Mode sendMailMode = Editor.getModeList().getMode(SEND_MAIL_MODE);
278             if (sendMailMode != null)
279                 return sendMailMode.createBuffer(file);
280         }
281         // Local file.
282
return createBuffer(file, null, null);
283     }
284
285     protected static Buffer createBuffer(File file, File cache, String JavaDoc listing)
286     {
287         Compression compression = null;
288         int fileType = Utilities.getFileType(cache != null ? cache : file);
289         if (fileType == FILETYPE_GZIP) {
290             // If we're looking at a remote file, gunzip the cached copy
291
// of it; otherwise, gunzip the file itself into the cache.
292
File uncompressed = cacheGZIP(cache != null ? cache : file);
293             if (uncompressed != null) {
294                 cache = uncompressed;
295                 fileType = Utilities.getFileType(cache);
296                 compression = new Compression(COMPRESSION_GZIP);
297             } else
298                 fileType = FILETYPE_BINARY; // Something went wrong.
299
}
300         if (fileType == FILETYPE_JPEG ||
301             Editor.getModeList().modeAccepts(IMAGE_MODE, file.getName())) {
302             Buffer buffer =
303                 ImageBuffer.createImageBuffer(file, cache, listing);
304             if (buffer != null) {
305                 buffer.setFileType(fileType);
306                 return buffer;
307             }
308         }
309         // Normal case.
310
Buffer buffer = new Buffer(file);
311         Debug.assertTrue(Editor.getBufferList().contains(buffer));
312         buffer.setFileType(fileType);
313         buffer.setCache(cache);
314         buffer.setListing(listing);
315         buffer.setCompression(compression);
316         if (file.isLocal() && !file.isFile())
317             buffer.setNewFile(true);
318         return buffer;
319     }
320
321     // For Session.createBuffers().
322
public static Buffer precreateBuffer(File file)
323     {
324         if (file == null) {
325             Debug.bug();
326             return null;
327         }
328         if (file.isRemote()) {
329             Debug.bug();
330             return null;
331         }
332         // Special case for unsent messages.
333
File dir = file.getParentFile();
334         if (dir != null && dir.equals(Directories.getDraftsFolder())) {
335             Mode sendMailMode = Editor.getModeList().getMode(SEND_MAIL_MODE);
336             if (sendMailMode != null)
337                 return sendMailMode.createBuffer(file);
338         }
339         // Normal case.
340
return new Buffer(file);
341     }
342
343     private boolean initialized;
344
345     public synchronized boolean initialized()
346     {
347         return initialized;
348     }
349
350     public synchronized void setInitialized(boolean b)
351     {
352         initialized = b;
353     }
354
355     public synchronized void initialize()
356     {
357         Debug.assertTrue(!initialized);
358         final File file = getFile();
359         if (fileType == FILETYPE_UNKNOWN) {
360             fileType = Utilities.getFileType(cache != null ? cache : file);
361             if (fileType == FILETYPE_GZIP) {
362                 // If we're looking at a remote file, gunzip the cached copy
363
// of it; otherwise, gunzip the file itself into the cache.
364
File uncompressed = cacheGZIP(cache != null ? cache : file);
365                 if (uncompressed != null) {
366                     cache = uncompressed;
367                     fileType = Utilities.getFileType(cache);
368                     compression = new Compression(COMPRESSION_GZIP);
369                 } else
370                     fileType = FILETYPE_BINARY; // Something went wrong.
371
}
372         }
373         mode = getDefaultMode();
374         formatter = mode.getFormatter(this);
375         if (fileType == FILETYPE_ZIP) {
376             supportsUndo = false;
377             type = TYPE_ARCHIVE;
378             readOnly = true;
379         } else if (fileType == FILETYPE_BINARY) {
380             readOnly = true;
381         } else if (fileType == FILETYPE_WORD) {
382             readOnly = true;
383         } else if (file != null) {
384             FileHistoryEntry entry =
385                 FileHistory.getFileHistory().findEntry(file.netPath());
386             if (entry != null) {
387                 // Set encoding.
388
final String JavaDoc encoding = entry.getEncoding();
389                 if (encoding != null && Utilities.isSupportedEncoding(encoding))
390                     file.setEncoding(encoding);
391                 // Set mode.
392
mode =
393                     Editor.getModeList().getModeFromModeName(entry.getMode());
394                 if (mode == null)
395                     mode = Editor.getModeList().getMode(PLAIN_TEXT_MODE);
396                 else if (mode.getId() == BINARY_MODE)
397                     readOnly = true;
398                 formatter = mode.getFormatter(this);
399                 // Properties from FileHistoryEntry override defaults set by
400
// mode.
401
properties.putAll(entry.getProperties());
402             }
403         }
404         if (file != null) {
405             if (file.getProtocol() == File.PROTOCOL_HTTP ||
406                 file.getProtocol() == File.PROTOCOL_HTTPS)
407                 readOnly = true;
408         }
409         initialized = true;
410     }
411
412     public Mode getDefaultMode()
413     {
414         final File file = getFile();
415         final ModeList modeList = Editor.getModeList();
416         switch (fileType) {
417             case FILETYPE_XML:
418                 return modeList.getMode(XML_MODE);
419             case FILETYPE_SHELLSCRIPT: {
420                 Mode m = grovelModeFromFile(file);
421                 if (m != null)
422                     return m;
423                 return modeList.getMode(SHELL_SCRIPT_MODE);
424             }
425             case FILETYPE_PERL:
426                 return modeList.getMode(PERL_MODE);
427             case FILETYPE_PHP:
428                 return modeList.getMode(PHP_MODE);
429             case FILETYPE_ZIP:
430                 return modeList.getMode(ARCHIVE_MODE);
431             case FILETYPE_GZIP:
432             case FILETYPE_BINARY:
433                 return modeList.getMode(BINARY_MODE);
434             case FILETYPE_JPEG:
435                 return modeList.getMode(IMAGE_MODE);
436             case FILETYPE_WORD:
437                 return modeList.getMode(WORD_MODE);
438             case FILETYPE_TEXT:
439             default: {
440                 Mode m = grovelModeFromFile(file);
441                 if (m == null) {
442                     if (compression != null && compression.getType() == COMPRESSION_ZIP) {
443                         String JavaDoc entryName = compression.getEntryName();
444                         if (entryName != null)
445                             m = getModeForFileName(entryName);
446                     } else if (file != null) {
447                         m = getModeForFileName(file.getName());
448                     }
449                     if (m != null && m.getId() == IMAGE_MODE) {
450                         if (fileType == FILETYPE_TEXT)
451                             m = modeList.getMode(PLAIN_TEXT_MODE);
452                         else
453                             m = modeList.getMode(BINARY_MODE);
454                     } else if (m == null) {
455                         if (file != null) {
456                             if (file.getProtocol() == File.PROTOCOL_HTTP ||
457                                 file.getProtocol() == File.PROTOCOL_HTTPS)
458                                 m = modeList.getMode(HTML_MODE);
459                         }
460                         if (m == null)
461                             m = modeList.getMode(PLAIN_TEXT_MODE);
462                     }
463                 }
464                 return m;
465             }
466         }
467     }
468
469     private static final Mode grovelModeFromFile(File file)
470     {
471         if (file == null)
472             return null;
473         if (!file.isLocal())
474             return null;
475         if (!file.isFile())
476             return null;
477         Mode mode = null;
478         try {
479             BufferedReader JavaDoc reader =
480                 new BufferedReader JavaDoc(new InputStreamReader JavaDoc(file.getInputStream()));
481             String JavaDoc s = reader.readLine();
482             if (s != null) {
483                 mode = grovelModeFromString(s);
484                 if (mode == null && s.startsWith("#!")) {
485                     // Consider second line too.
486
s = reader.readLine();
487                     if (s != null)
488                         mode = grovelModeFromString(s);
489                 }
490             }
491             reader.close();
492         }
493         catch (IOException JavaDoc e) {
494             Log.error(e);
495         }
496         return mode;
497     }
498
499     private static final Mode grovelModeFromString(String JavaDoc s)
500     {
501         if (s != null) {
502             int begin = s.indexOf("-*-");
503             if (begin >= 0) {
504                 s = s.substring(begin + 3);
505                 int end = s.indexOf("-*-");
506                 if (end >= 0) {
507                     s = s.substring(0, end).trim().toLowerCase();
508                     int index = s.indexOf("mode:");
509                     String JavaDoc modeName;
510                     if (index < 0) {
511                         // "-*- Lisp -*-"
512
modeName = s;
513                     } else {
514                         // "-*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-"
515
s = s.substring(5).trim();
516                         for (end = 0; end < s.length(); end++) {
517                             char c = s.charAt(end);
518                             if (c == ' ' || c == '\t' || c == ';')
519                                 break;
520                         }
521                         modeName = s.substring(0, end);
522                     }
523                     return Editor.getModeList().getModeFromModeName(modeName);
524                 }
525             }
526         }
527         return null;
528     }
529
530     // Locking.
531
public final boolean isInUse()
532     {
533         return mutex.isInUse();
534     }
535
536     public void acquire() throws InterruptedException JavaDoc
537     {
538         mutex.acquire();
539     }
540
541     public synchronized void release()
542     {
543         mutex.release();
544     }
545
546     public boolean attempt() throws InterruptedException JavaDoc
547     {
548         return mutex.attempt();
549     }
550
551     public boolean attempt(long msecs) throws InterruptedException JavaDoc
552     {
553         return mutex.attempt(msecs);
554     }
555
556     public final boolean isLocked()
557     {
558         return isInUse();
559     }
560
561     public synchronized boolean lock()
562     {
563         try {
564             return attempt();
565         }
566         catch (InterruptedException JavaDoc e) {
567             return false;
568         }
569     }
570
571     public synchronized void unlock()
572     {
573         release();
574     }
575
576     public final void lockRead() throws InterruptedException JavaDoc
577     {
578         rwlock.lockRead();
579     }
580
581     public final void unlockRead()
582     {
583         rwlock.unlockRead();
584     }
585
586     public final void lockWrite() throws InterruptedException JavaDoc
587     {
588         rwlock.lockWrite();
589     }
590
591     public final void unlockWrite()
592     {
593         rwlock.unlockWrite();
594     }
595
596     public final boolean isWriteLocked()
597     {
598         return rwlock.isWriteLocked();
599     }
600
601     public boolean isVisible()
602     {
603         for (EditorIterator it = new EditorIterator(); it.hasNext();)
604             if (it.nextEditor().getBuffer() == this)
605                 return true;
606
607         return false;
608     }
609
610     public void setWaitCursor()
611     {
612         for (EditorIterator it = new EditorIterator(); it.hasNext();) {
613             Editor ed = it.nextEditor();
614             if (ed.getBuffer() == this)
615                 ed.setWaitCursor();
616         }
617     }
618
619     public void setDefaultCursor()
620     {
621         for (EditorIterator it = new EditorIterator(); it.hasNext();) {
622             Editor ed = it.nextEditor();
623             if (ed.getBuffer() == this)
624                 ed.setDefaultCursor();
625         }
626     }
627
628     public final PropertyList getProperties()
629     {
630         return properties;
631     }
632
633     public final void setCache(File file)
634     {
635         cache = file;
636     }
637
638     public final File getCache()
639     {
640         return cache;
641     }
642
643     public final Formatter getFormatter()
644     {
645         return formatter;
646     }
647
648     public final void setFormatter(Formatter formatter)
649     {
650         this.formatter = formatter;
651     }
652
653     public boolean isReadOnly()
654     {
655         return readOnly || forceReadOnly;
656     }
657
658     private boolean busy;
659
660     public synchronized final boolean isBusy()
661     {
662         return busy;
663     }
664
665     public synchronized final void setBusy(boolean b)
666     {
667         busy = b;
668     }
669
670     public final BackgroundProcess getBackgroundProcess()
671     {
672         return backgroundProcess;
673     }
674
675     public final void setBackgroundProcess(BackgroundProcess backgroundProcess)
676     {
677         this.backgroundProcess = backgroundProcess;
678     }
679
680     public File getCurrentDirectory()
681     {
682         return getFile() != null ? getFile().getParentFile() : Directories.getUserHomeDirectory();
683     }
684
685     // Subclasses should override this method if appropriate!
686
public File getCompletionDirectory()
687     {
688         final File file = getFile();
689         if (file != null) {
690             if (file.isLocal() || file instanceof SshFile)
691                 return file.isDirectory() ? file : file.getParentFile();
692         }
693         return Directories.getUserHomeDirectory();
694     }
695
696     public View getInitialView()
697     {
698         View view = new View();
699         view.setDot(getInitialDotPos());
700         return view;
701     }
702
703     private int initialLineNumber;
704     private int initialOffset;
705
706     public Position getInitialDotPos()
707     {
708         Line line = getLine(initialLineNumber);
709         if (line == null) {
710             line = getFirstLine();
711             return line != null ? new Position(line, 0) : null;
712         }
713         return new Position(line, Math.min(initialOffset, line.length()));
714     }
715
716     public void setInitialDotPos(int lineNumber, int offset)
717     {
718         initialLineNumber = lineNumber;
719         initialOffset = offset;
720     }
721
722     private long lastActivated;
723
724     public final long getLastActivated()
725     {
726         return lastActivated;
727     }
728
729     public final void setLastActivated(long l)
730     {
731         lastActivated = l;
732     }
733
734     public final int getLineCount()
735     {
736         return lineCount;
737     }
738
739     public final int getModCount()
740     {
741         return modCount;
742     }
743
744     public final synchronized void setModCount(int count)
745     {
746         if (count != modCount) {
747             modCount = count;
748             srText = null;
749         }
750     }
751
752     public final synchronized void incrementModCount()
753     {
754         ++modCount;
755         srText = null;
756     }
757
758     public final void setModCountWhenLastSaved(int count)
759     {
760         autosaveModCount = count;
761     }
762
763     public final KeyMap getKeyMapForMode()
764     {
765         // Should never return null.
766
return mode.getKeyMap();
767     }
768
769     public final int getFileType()
770     {
771         return fileType;
772     }
773
774     private final void setFileType(int fileType)
775     {
776         this.fileType = fileType;
777     }
778
779     private static File cacheGZIP(File f)
780     {
781         try {
782             File tempFile = Utilities.getTempFile();
783             if (tempFile != null) {
784                 InputStream JavaDoc in = new GZIPInputStream JavaDoc(f.getInputStream());
785                 if (in != null) {
786                     OutputStream JavaDoc out = tempFile.getOutputStream();
787                     byte[] buf = new byte[4096];
788                     int bytesRead;
789                     while ((bytesRead = in.read(buf)) > 0)
790                         out.write(buf, 0, bytesRead);
791                     out.close();
792                     in.close();
793                     return tempFile;
794                 }
795             }
796         }
797         catch (IOException JavaDoc e) {
798             Log.error(e);
799         }
800         return null;
801     }
802
803     // Handles all the paperwork when we rename a buffer.
804
public void changeFile(File f)
805     {
806         if (f == null)
807             return;
808         String JavaDoc newName = f.canonicalPath();
809         if (newName == null)
810             return;
811         File oldFile = getFile();
812         String JavaDoc oldName = oldFile != null ? oldFile.canonicalPath() : null;
813         setFile(f);
814         setCompression(null);
815         type = TYPE_NORMAL;
816         title = null;
817         Mode oldMode = mode;
818         if (oldMode == PlainTextMode.getMode()) {
819             setModeFromFilename(newName);
820             if (mode != oldMode) {
821                 // Mode has changed.
822
needsParsing = true;
823
824                 // Make sure we parse the buffer before we display it.
825
if (formatter != null)
826                     formatter.parseBuffer();
827             }
828         }
829         readOnly = false;
830         isUntitled = false;
831         if (oldName != null)
832             Autosave.rename(oldName, newName);
833         Editor.getBufferList().modified();
834         Sidebar.setUpdateFlagInAllFrames(SIDEBAR_BUFFER_LIST_CHANGED);
835         for (EditorIterator it = new EditorIterator(); it.hasNext();) {
836             Editor editor = it.nextEditor();
837             if (editor.getBuffer() == this) {
838                 editor.updateLocation();
839                 editor.getDisplay().repaint();
840             }
841         }
842     }
843
844     protected void setModeFromFilename(String JavaDoc filename)
845     {
846         mode = Editor.getModeList().getModeForFileName(filename);
847         if (mode == null)
848             mode = Editor.getModeList().getMode(PLAIN_TEXT_MODE);
849         formatter = mode.getFormatter(this);
850     }
851
852     private static Mode getModeForFileName(String JavaDoc filename)
853     {
854         if (filename.endsWith(".gz"))
855             filename = filename.substring(0, filename.length() - 3);
856
857         // Strip path prefix (if any).
858
// Look for '/' on both Windows and Unix (filename might be a ZipEntry name).
859
int index = filename.lastIndexOf('/');
860         if (index >= 0)
861             filename = filename.substring(index + 1);
862
863         // Look for '\' on Windows only.
864
if (Platform.isPlatformWindows()) {
865             index = filename.lastIndexOf('\\');
866             if (index >= 0)
867                 filename = filename.substring(index + 1);
868         }
869
870         return Editor.getModeList().getModeForFileName(filename);
871     }
872
873     public final void setMode(Mode mode)
874     {
875         this.mode = mode;
876         formatter = mode.getFormatter(this);
877     }
878
879     public void changeMode(Mode newMode)
880     {
881         final int oldModeId = mode.getId();
882         if (oldModeId == DIRECTORY_MODE)
883             return;
884         final int newModeId = newMode.getId();
885         if (newModeId != oldModeId) {
886             // Must reload buffer if changing into or out of binary mode.
887
boolean reloading = newModeId == BINARY_MODE || oldModeId == BINARY_MODE;
888             mode = newMode;
889             formatter = mode.getFormatter(this);
890             if (reloading) {
891                 reload();
892                 if (newModeId == IMAGE_MODE)
893                     return;
894             }
895
896             if (newModeId == BINARY_MODE) {
897                 readOnly = true;
898             } else {
899                 final File file = getFile();
900                 if (file != null && !file.isRemote() && file.isFile())
901                     readOnly = !file.canWrite();
902             }
903
904             formatter.parseBuffer();
905
906             for (EditorIterator it = new EditorIterator(); it.hasNext();) {
907                 Editor ed = it.nextEditor();
908                 if (ed.getBuffer() == this) {
909                     if (reloading) {
910                         ed.setDot(getFirstLine(), 0);
911                         ed.setMark(null);
912                         ed.setTopLine(getFirstLine());
913                         ed.moveCaretToDotCol();
914                         ed.updateLocation();
915                     }
916                     ed.setUpdateFlag(REPAINT);
917                     ed.updateDisplay();
918                 }
919             }
920         }
921     }
922
923     public final String JavaDoc getCommentStart()
924     {
925         return mode.getCommentStart();
926     }
927
928     public final String JavaDoc getCommentEnd()
929     {
930         return mode.getCommentEnd();
931     }
932
933     private long lastModified;
934
935     public final long getLastModified()
936     {
937         return lastModified;
938     }
939
940     public final void setLastModified(long lastModified)
941     {
942         this.lastModified = lastModified;
943     }
944
945     protected void loadFile(File toBeLoaded)
946     {
947         try {
948             int modeId = getModeId();
949             if (modeId == ARCHIVE_MODE || modeId == WORD_MODE || modeId == XML_MODE)
950                 mode.loadFile(this, toBeLoaded);
951             else {
952                 final String JavaDoc encoding = toBeLoaded.getEncoding();
953                 load(toBeLoaded.getInputStream(), encoding);
954                 if (encoding != null)
955                     saveProperties(); // Remember encoding for next time.
956
}
957         }
958         catch (IOException JavaDoc e) {
959             Log.error(e);
960         }
961         // Handle zero length files.
962
if (getFirstLine() == null) {
963             appendLine("");
964             lineSeparator = System.getProperty("line.separator");
965         }
966         final File file = getFile();
967         if (file != null && file.getProtocol() != File.PROTOCOL_HTTP)
968             lastModified = toBeLoaded.lastModified();
969         renumberOriginal();
970     }
971
972     public int load()
973     {
974         if (!isLoaded()) {
975             try {
976                 lockWrite();
977             }
978             catch (InterruptedException JavaDoc e) {
979                 Log.error(e);
980                 return LOAD_FAILED;
981             }
982             try {
983                 final File file = getFile();
984                 final File toBeLoaded = cache != null ? cache : file;
985                 if (toBeLoaded.isFile()) {
986                     Editor editor = Editor.currentEditor();
987                     FastStringBuffer sb = new FastStringBuffer("Loading");
988                     if (compression != null) {
989                         if (compression.getType() == COMPRESSION_ZIP) {
990                             String JavaDoc entryName = compression.getEntryName();
991                             if (entryName != null) {
992                                 sb.append(' ');
993                                 sb.append(entryName);
994                             }
995                         }
996                     } else if (file != null) {
997                         sb.append(' ');
998                         sb.append(file.getName());
999                     }
1000                    sb.append("...");
1001                    editor.status(sb.toString());
1002                    Debug.assertTrue(mode != null);
1003                    Debug.assertTrue(toBeLoaded != null);
1004                    loadFile(toBeLoaded);
1005                    // At this point, if we loaded from a cache, lastModified will
1006
// be set based on the cache file, which is not what we want
1007
// in the case of a local file.
1008
if (toBeLoaded == cache && file != null && !file.isRemote())
1009                        lastModified = file.lastModified();
1010                    formatter.parseBuffer();
1011                    checkCVS();
1012                    sb.append("done");
1013                    editor.status(sb.toString());
1014                } else {
1015                    // File doesn't exist.
1016
if (getFirstLine() == null) {
1017                        appendLine("");
1018                        lineSeparator = System.getProperty("line.separator");
1019                    }
1020                    renumberOriginal();
1021                    setModeFromFilename(file.canonicalPath());
1022                    setLoaded(true);
1023                }
1024            }
1025            catch (OutOfMemoryError JavaDoc e) {
1026                _empty();
1027                throw e;
1028            }
1029            finally {
1030                unlockWrite();
1031            }
1032        }
1033        return LOAD_COMPLETED;
1034    }
1035
1036    private void reloadSucceeded()
1037    {
1038        Debug.assertTrue(!rwlock.isWriteLocked());
1039        Debug.assertTrue(SwingUtilities.isEventDispatchThread());
1040        for (EditorIterator it = new EditorIterator(); it.hasNext();) {
1041            Editor ed = it.nextEditor();
1042            if (ed.getBuffer() != this)
1043                continue;
1044            View view = ed.getView(this);
1045            if (view != null) {
1046                Line dotLine = getLine(view.getDotLineNumber());
1047                if (dotLine == null)
1048                    dotLine = getFirstLine();
1049                if (dotLine != null) {
1050                    ed.setDot(dotLine, 0);
1051                    ed.moveCaretToDotCol();
1052                }
1053                Line topLine = getLine(view.getTopLineNumber());
1054                if (topLine == null)
1055                    topLine = dotLine;
1056                ed.setTopLine(topLine);
1057            } else {
1058                ed.setDot(getFirstLine(), 0);
1059                ed.moveCaretToDotCol();
1060                ed.setTopLine(getFirstLine());
1061            }
1062            ed.setUpdateFlag(REPAINT);
1063            ed.updateDisplay();
1064        }
1065        // The buffer is unmodified now, so we need to update its icon in the
1066
// buffer list(s).
1067
Sidebar.setUpdateFlagInAllFrames(SIDEBAR_REPAINT_BUFFER_LIST);
1068    }
1069
1070    private void reloadFailed()
1071    {
1072        MessageDialog.showMessageDialog("Reload failed", "Error");
1073    }
1074
1075    public void reload()
1076    {
1077        final File file = getFile();
1078        if (file == null)
1079            return;
1080        switch (file.getProtocol()) {
1081            case File.PROTOCOL_FTP:
1082                reloadFtp((FtpFile) file);
1083                return;
1084            case File.PROTOCOL_HTTP:
1085            case File.PROTOCOL_HTTPS:
1086                reloadHttp((HttpFile) file);
1087                return;
1088            case File.PROTOCOL_FILE:
1089                if (file.isFile()) {
1090                    // Local file.
1091
if (getModeId() == ARCHIVE_MODE) {
1092                        empty();
1093                        resetUndo();
1094                        mode.loadFile(this, file);
1095                    } else
1096                        reloadLocal(file);
1097                }
1098                reloadSucceeded();
1099                return;
1100            default:
1101                Debug.assertTrue(false);
1102                break;
1103        }
1104    }
1105
1106    // Asynchronous.
1107
private void reloadFtp(FtpFile file)
1108    {
1109        Log.debug("reloadFtp");
1110        Debug.assertTrue(SwingUtilities.isEventDispatchThread());
1111        FtpSession session = FtpSession.getSession(file);
1112        final FtpLoadProcess ftpLoadProcess = new FtpLoadProcess(this, file, session);
1113        Runnable JavaDoc successRunnable = new Runnable JavaDoc() {
1114            public void run()
1115            {
1116                File newCache = ftpLoadProcess.getCache();
1117                if (newCache != null) {
1118                    Log.debug("newCache != null");
1119                    if (cache != null && cache.isFile())
1120                        cache.delete();
1121                    cache = newCache;
1122                    reloadLocal(cache);
1123                } else {
1124                    // User cancelled.
1125
setLoaded(true);
1126                }
1127                setBusy(false);
1128                reloadSucceeded();
1129            }
1130        };
1131        ErrorRunnable errorRunnable = new ErrorRunnable("Reload failed") {
1132            public void run()
1133            {
1134                setBusy(false);
1135                reloadFailed();
1136            }
1137        };
1138        ftpLoadProcess.setProgressNotifier(new StatusBarProgressNotifier(this));
1139        ftpLoadProcess.setSuccessRunnable(successRunnable);
1140        ftpLoadProcess.setErrorRunnable(errorRunnable);
1141        ftpLoadProcess.start();
1142    }
1143
1144    private void reloadHttp(HttpFile file)
1145    {
1146        Log.debug("reloadHttp");
1147        Debug.assertTrue(SwingUtilities.isEventDispatchThread());
1148        final HttpLoadProcess httpLoadProcess = new HttpLoadProcess(this, file);
1149        Runnable JavaDoc successRunnable = new Runnable JavaDoc() {
1150            public void run()
1151            {
1152                File newCache = httpLoadProcess.getCache();
1153                if (newCache != null) {
1154                    Log.debug("newCache != null");
1155                    if (cache != null && cache.isFile())
1156                        cache.delete();
1157                    cache = newCache;
1158                    reloadLocal(cache);
1159                } else {
1160                    // User cancelled.
1161
setLoaded(true);
1162                }
1163                setBusy(false);
1164                reloadSucceeded();
1165            }
1166        };
1167        ErrorRunnable errorRunnable = new ErrorRunnable("Reload failed") {
1168            public void run()
1169            {
1170                setBusy(false);
1171                reloadFailed();
1172            }
1173        };
1174        httpLoadProcess.setProgressNotifier(new StatusBarProgressNotifier(this));
1175        httpLoadProcess.setSuccessRunnable(successRunnable);
1176        httpLoadProcess.setErrorRunnable(errorRunnable);
1177        setBusy(true);
1178        new Thread JavaDoc(httpLoadProcess).start();
1179    }
1180
1181    private void reloadLocal(File file)
1182    {
1183        try {
1184            lockWrite();
1185        }
1186        catch (InterruptedException JavaDoc e) {
1187            Log.error(e);
1188            return;
1189        }
1190        try {
1191            empty();
1192            loadFile(file);
1193            formatter.parseBuffer();
1194        }
1195        finally {
1196            unlockWrite();
1197        }
1198        unmodified();
1199        deleteAutosaveFile();
1200        resetUndo();
1201    }
1202
1203    public synchronized void kill()
1204    {
1205        BufferList bufferList = Editor.getBufferList();
1206        if (!bufferList.contains(this)) {
1207            Debug.bug("buffer.kill() buffer not in list");
1208            return;
1209        }
1210
1211        Marker.invalidateMarkers(this);
1212
1213        Buffer buf = bufferList.getPreviousPrimaryBuffer(this);
1214        if (buf != null && buf.isPaired()) {
1215            Buffer secondary = buf.getSecondary();
1216            if (secondary != null) {
1217                if (secondary.getLastActivated() > buf.getLastActivated())
1218                    buf = secondary;
1219            }
1220        }
1221        if (buf == null)
1222            buf = new Directory(getCurrentDirectory());
1223        // Copy editor list since switchToBuffer() may close an editor.
1224
ArrayList JavaDoc editors = new ArrayList JavaDoc();
1225        for (EditorIterator it = new EditorIterator(); it.hasNext();)
1226            editors.add(it.next());
1227        for (Iterator JavaDoc it = editors.iterator(); it.hasNext();) {
1228            Editor ed = (Editor) it.next();
1229            // Skip editor if it has been closed.
1230
if (Editor.getEditorList().contains(ed)) {
1231                if (ed.getBuffer() == this)
1232                    ed.switchToBuffer(buf);
1233                ed.removeView(this);
1234            }
1235        }
1236        deleteAutosaveFile();
1237        bufferList.remove(this);
1238        dispose();
1239        Sidebar.setUpdateFlagInAllFrames(SIDEBAR_BUFFER_LIST_CHANGED |
1240            SIDEBAR_MODIFIED_BUFFER_COUNT);
1241    }
1242
1243    public synchronized void relink()
1244    {
1245        BufferList bufferList = Editor.getBufferList();
1246        if (bufferList.contains(this)) {
1247            Debug.bug();
1248            return;
1249        }
1250        bufferList.add(this);
1251    }
1252
1253    private Thread JavaDoc startTaggerThread(int priority)
1254    {
1255        if (mode != null) {
1256            Tagger tagger = mode.getTagger(this);
1257            if (tagger != null) {
1258                Thread JavaDoc thread = new Thread JavaDoc(tagger);
1259                thread.setPriority(priority);
1260                thread.setDaemon(true);
1261                thread.setName("tagger " + getFile().getName());
1262                thread.start();
1263                return thread;
1264            }
1265        }
1266        return null;
1267    }
1268
1269    public boolean isTaggable()
1270    {
1271        final File file = getFile();
1272        if (file == null)
1273            return false;
1274        if (file.isRemote())
1275            return false;
1276        return mode.isTaggable();
1277    }
1278
1279    // Runs tagger if tags == null.
1280
public List JavaDoc getTags(boolean update)
1281    {
1282        List JavaDoc tags = getTags();
1283        if (tags != null)
1284            return tags;
1285        if (mode != null) {
1286            Tagger tagger = mode.getTagger(this);
1287            if (tagger != null)
1288                tagger.run();
1289        }
1290        return getTags();
1291    }
1292
1293    public boolean saveToCache()
1294    {
1295        if (lineSeparator == null)
1296            lineSeparator = System.getProperty("line.separator");
1297        final File tempFile = Utilities.getTempFile();
1298        if (tempFile != null) {
1299            File file = getFile();
1300            if (file != null)
1301                tempFile.setEncoding(file.getEncoding());
1302            if (writeFile(tempFile)) {
1303                final File oldCache = cache;
1304                cache = tempFile;
1305                if (oldCache != null && oldCache.isFile())
1306                    oldCache.delete();
1307                return true;
1308            }
1309        }
1310        return false;
1311    }
1312
1313    private boolean maybeWriteBackupFromCache()
1314    {
1315        if (cache == null) {
1316            Log.error("maybeWriteBackupFromCache cache is null");
1317            return false;
1318        }
1319        if (!cache.isFile()) {
1320            Log.error("maybeWriteBackupFromCache cache is not a file");
1321            return false;
1322        }
1323        if (!cache.canRead()) {
1324            Log.error("maybeWriteBackupFromCache cache is not readable");
1325            return false;
1326        }
1327        if (backedUp)
1328            return true; // We only want to do the backup once!
1329
File file = getFile();
1330        if (file == null) {
1331            Debug.bug();
1332            return false;
1333        }
1334        String JavaDoc name = file.getName();
1335        if (compression != null && compression.getType() == COMPRESSION_GZIP) {
1336            if (name.endsWith(".gz"))
1337                name = name.substring(0, name.length() - 3);
1338        }
1339        return backedUp =
1340            Utilities.makeBackup(cache, name, false);
1341    }
1342
1343    public boolean save()
1344    {
1345        Debug.assertTrue(SwingUtilities.isEventDispatchThread());
1346        if (!isModified())
1347            return true;
1348        addUndoBoundary();
1349        boolean succeeded = false;
1350        final File file = getFile();
1351        if (file != null) {
1352            if (file.isLocal())
1353                succeeded = saveLocal(file);
1354            if (file instanceof FtpFile)
1355                succeeded = saveFtp();
1356            if (file instanceof SshFile)
1357                succeeded = saveSsh((SshFile)file);
1358        }
1359        if (succeeded && Editor.isLispInitialized())
1360            LispAPI.invokeAfterSaveHook(this);
1361        return succeeded;
1362    }
1363
1364    private boolean saveLocal(final File file)
1365    {
1366        Debug.assertTrue(file.isLocal());
1367
1368        if (compression != null && compression.getType() == COMPRESSION_GZIP)
1369            return saveLocalCompressed(file);
1370
1371        try {
1372            writeBuffer();
1373            renumberOriginal();
1374            saved();
1375            lastModified = file.lastModified();
1376        }
1377        catch (SaveException e) {
1378            final String JavaDoc where = "Save";
1379            String JavaDoc message = e.getMessage();
1380            // Tell user exactly what error occurred.
1381
if (message != null)
1382                MessageDialog.showMessageDialog(message, where);
1383            // Display summary message.
1384
message = "Unable to save " + file.canonicalPath();
1385            MessageDialog.showMessageDialog(message, where);
1386            return false;
1387        }
1388
1389        if (isTaggable())
1390            Editor.getTagFileManager().addToQueue(file.getParentFile(), mode);
1391        startTaggerThread(Thread.MIN_PRIORITY);
1392        boolean repaint = false;
1393        final ModeList modeList = Editor.getModeList();
1394        if (file.equals(Preferences.getPreferencesFile())) {
1395            // Reload preferences.
1396
Editor.loadPreferences();
1397            if (Editor.preferences().getBooleanProperty(Property.AUTO_RELOAD_KEY_MAPS)) {
1398                // Reload keymaps.
1399
KeyMap.reloadKeyMaps();
1400            }
1401            repaint = true;
1402        } else {
1403            if (Editor.preferences().getBooleanProperty(Property.AUTO_RELOAD_KEY_MAPS)) {
1404                // Reload mode-specific keymap(s) if modified.
1405
synchronized (modeList) {
1406                    for (Iterator JavaDoc it = modeList.iterator(); it.hasNext();) {
1407                        ModeListEntry entry = (ModeListEntry) it.next();
1408                        Mode mode = entry.getMode(false);
1409                        if (mode != null) {
1410                            if (file.equals(mode.getKeyMapFile()))
1411                                mode.deleteKeyMap();
1412                        }
1413                    }
1414                }
1415                // Global keymap.
1416
if (file.equals(KeyMap.getGlobalKeyMapFile()))
1417                    KeyMap.deleteGlobalKeyMap();
1418            }
1419        }
1420        // Reload theme if modified.
1421
String JavaDoc theme = Editor.preferences().getStringProperty(Property.THEME);
1422        if (theme != null) {
1423            if (Utilities.isFilenameAbsolute(theme)) {
1424                if (file.canonicalPath().equals(File.getInstance(theme).canonicalPath())) {
1425                    Editor.loadPreferences();
1426                    repaint = true;
1427                }
1428            } else if (file.getName().equals(theme)) {
1429                Editor.loadPreferences();
1430                repaint = true;
1431            }
1432        }
1433
1434        // Reload aliases if modified.
1435
if (file.equals(Editor.getAliasesFile()))
1436            Editor.reloadAliases();
1437
1438        // Update listRegisters buffer (if any).
1439
File parent = file.getParentFile();
1440        if (parent != null && parent.equals(Directories.getRegistersDirectory())) {
1441            Buffer buf = Registers.findListRegistersBuffer();
1442            if (buf != null)
1443                buf.reload();
1444        }
1445
1446        checkCVS();
1447
1448        if (repaint) {
1449            // Force formatters to be re-initialized.
1450
for (BufferIterator it = new BufferIterator(); it.hasNext();) {
1451                Buffer buf = it.nextBuffer();
1452                if (buf.getFormatter() != null)
1453                    buf.getFormatter().reset();
1454            }
1455            Display.initializeStaticValues();
1456            for (int i = 0; i < Editor.getFrameCount(); i++) {
1457                Frame f = Editor.getFrame(i);
1458                if (f != null) {
1459                    f.resetDisplay();
1460                    f.repaint();
1461                }
1462            }
1463            Editor.restoreFocus();
1464        }
1465        return true;
1466    }
1467
1468    private boolean saveLocalCompressed(File file)
1469    {
1470        final String JavaDoc dialogTitle = "Save";
1471        final Editor editor = Editor.currentEditor();
1472        // Do this before saving changes to cache!
1473
if (!maybeWriteBackupFromCache()) {
1474            FastStringBuffer sb =
1475                new FastStringBuffer("Unable to write backup file for ");
1476            sb.append(file.getName());
1477            sb.append(". Save anyway?");
1478            if (!editor.confirm(dialogTitle, sb.toString()))
1479                return false;
1480        }
1481        if (!saveToCache()) {
1482            FastStringBuffer sb =
1483                new FastStringBuffer("Unable to write temporary file for ");
1484            sb.append(file.getName());
1485            MessageDialog.showMessageDialog(sb.toString(), dialogTitle);
1486            return false;
1487        }
1488        if (!compress(cache, file))
1489            return false;
1490        saved();
1491        lastModified = file.lastModified();
1492        return true;
1493    }
1494
1495    private boolean compress(File source, File destination)
1496    {
1497        if (source == null) {
1498            Debug.bug();
1499            return false;
1500        }
1501        if (destination == null) {
1502            Debug.bug();
1503            return false;
1504        }
1505        if (!source.isFile()) {
1506            Debug.bug();
1507            return false;
1508        }
1509        final File tempFile = Utilities.getTempFile(destination.getParentFile());
1510        try {
1511            final int bufSize = 4096;
1512            BufferedInputStream JavaDoc in =
1513                new BufferedInputStream JavaDoc(source.getInputStream());
1514            GZIPOutputStream JavaDoc out =
1515                new GZIPOutputStream JavaDoc(new BufferedOutputStream JavaDoc(tempFile.getOutputStream()),
1516                                     bufSize);
1517            byte[] buffer = new byte[bufSize];
1518            while (true) {
1519                int bytesRead = in.read(buffer, 0, bufSize);
1520                if (bytesRead > 0)
1521                    out.write(buffer, 0, bytesRead);
1522                else
1523                    break;
1524            }
1525            in.close();
1526            out.flush();
1527            out.close();
1528            return Utilities.deleteRename(tempFile, destination);
1529        }
1530        catch (IOException JavaDoc e) {
1531            Log.error(e);
1532            return false;
1533        }
1534    }
1535
1536    private boolean saveFtp()
1537    {
1538        Debug.assertTrue(SwingUtilities.isEventDispatchThread());
1539        Debug.assertTrue(getFile() instanceof FtpFile);
1540        final FtpFile file = (FtpFile) getFile();
1541        final FtpSession session = FtpSession.getSession(file);
1542        if (session == null)
1543            return false;
1544        Debug.assertTrue(session.isLocked());
1545        if (!lock()) {
1546            session.unlock();
1547            MessageDialog.showMessageDialog("Buffer is busy", file.netPath());
1548            return false;
1549        }
1550        Debug.assertTrue(isLocked());
1551        final Editor editor = Editor.currentEditor();
1552        editor.setWaitCursor();
1553        // Do this before saving changes to cache!
1554
if (!maybeWriteBackupFromCache()) {
1555            editor.setDefaultCursor();
1556            String JavaDoc message = "Unable to write backup file for " +
1557                file.getName() + ". Save anyway?";
1558            if (!editor.confirm("Save", message)) {
1559                unlock();
1560                session.unlock();
1561                return false;
1562            }
1563            editor.setWaitCursor();
1564        }
1565        if (!saveToCache()) {
1566            unlock();
1567            session.unlock();
1568            editor.setDefaultCursor();
1569            String JavaDoc message = "Unable to write temporary file for " +
1570                file.getName();
1571            MessageDialog.showMessageDialog(message, "Save");
1572            return false;
1573        }
1574        final FtpSaveProcess saveProcess;
1575        if (compression != null && compression.getType() == COMPRESSION_GZIP) {
1576            final File tempFile = Utilities.getTempFile();
1577            if (!compress(cache, tempFile)) {
1578                String JavaDoc message = "Unable to compress temporary file for " +
1579                    file.getName();
1580                MessageDialog.showMessageDialog(message, "Save");
1581                return false;
1582            }
1583            saveProcess = new FtpSaveProcess(this, tempFile, file, session);
1584        } else
1585            saveProcess = new FtpSaveProcess(this, cache, file, session);
1586        saveProcess.setConfirmIfDestinationChanged(true);
1587        saveProcess.setTitle("Save");
1588        final Runnable JavaDoc successRunnable = new Runnable JavaDoc() {
1589            public void run()
1590            {
1591                saved();
1592                setListing(saveProcess.getListing());
1593                for (EditorIterator it = new EditorIterator(); it.hasNext();) {
1594                    Editor ed = it.nextEditor();
1595                    if (ed.getBuffer() == Buffer.this)
1596                        ed.setDefaultCursor();
1597                }
1598                Sidebar.repaintBufferListInAllFrames();
1599            }
1600        };
1601        saveProcess.setSuccessRunnable(successRunnable);
1602        Debug.assertTrue(isLocked());
1603        saveProcess.start();
1604        return true;
1605    }
1606
1607    private boolean saveSsh(final SshFile file)
1608    {
1609        final String JavaDoc title = "Save";
1610        final Editor editor = Editor.currentEditor();
1611        boolean succeeded = false;
1612        String JavaDoc message = null;
1613
1614        // Do this before saving changes to cache!
1615
if (!maybeWriteBackupFromCache()) {
1616            message = "Unable to write backup file for " + file.getName() +
1617                ". Save anyway?";
1618            if (!editor.confirm(title, message))
1619                return false;
1620        }
1621
1622        if (!saveToCache()) {
1623            message = "Unable to write temporary file for " + file.getName();
1624            MessageDialog.showMessageDialog(message, title);
1625            return false;
1626        }
1627
1628        Ssh ssh = new Ssh();
1629
1630        if (compression != null && compression.getType() == COMPRESSION_GZIP) {
1631            final File tempFile = Utilities.getTempFile();
1632            if (!compress(cache, tempFile)) {
1633                message = "Unable to compress temporary file for " +
1634                    file.getName();
1635                MessageDialog.showMessageDialog(message, "Save");
1636                return false;
1637            }
1638            succeeded = ssh.copy(tempFile, file);
1639        } else
1640            succeeded = ssh.copy(cache, file);
1641
1642        if (!succeeded)
1643            message = ssh.getErrorText();
1644
1645        if (succeeded) {
1646            saved();
1647        } else {
1648            // Tell user exactly what error occurred.
1649
if (message != null)
1650                MessageDialog.showMessageDialog(message, title);
1651
1652            // Display summary message.
1653
message = "Unable to save " + file.canonicalPath();
1654            MessageDialog.showMessageDialog(message, title);
1655        }
1656
1657        return succeeded;
1658    }
1659
1660    public void saveAs(File destination)
1661    {
1662        File file = getFile();
1663        if (file != null)
1664            destination.setEncoding(file.getEncoding());
1665        if (destination.isLocal())
1666            saveAsLocal(destination);
1667        else if (destination instanceof FtpFile)
1668            saveAsFtp((FtpFile)destination);
1669        else
1670            MessageDialog.showMessageDialog("Invalid destination", "Save As");
1671    }
1672
1673    private boolean saveAsLocal(File destination)
1674    {
1675        Debug.assertTrue(SwingUtilities.isEventDispatchThread());
1676        if (destination == null)
1677            return false;
1678        if (destination.isDirectory()) {
1679            final String JavaDoc prompt =
1680                destination.canonicalPath().concat(" is a directory");
1681            MessageDialog.showMessageDialog(prompt, "Save As");
1682            return false;
1683        }
1684        setBusy(true);
1685        Editor.currentEditor().setWaitCursor();
1686        boolean succeeded = false;
1687        String JavaDoc message = null;
1688        if (lineSeparator == null)
1689            lineSeparator = System.getProperty("line.separator");
1690        final File tempFile = Utilities.getTempFile(destination.getParent());
1691        if (tempFile == null) {
1692            message = "Unable to create temporary file for " +
1693                destination.canonicalPath();
1694        } else {
1695            tempFile.setEncoding(destination.getEncoding());
1696            if (writeFile(tempFile)) {
1697                Log.debug("buffer written to " + tempFile.canonicalPath());
1698                if (Utilities.makeBackup(destination, false)) {
1699                    destination.delete();
1700                    if (tempFile.renameTo(destination)) {
1701                        succeeded = true;
1702                    } else {
1703                        Log.error("unable to rename " +
1704                            tempFile.canonicalPath() + " to " +
1705                            destination.canonicalPath());
1706                        message = "Unable to rename temporary file";
1707                    }
1708                } else {
1709                    Log.error("backup failed");
1710                    message = "Unable to write backup file for " +
1711                        destination.canonicalPath();
1712                }
1713            } else {
1714                Log.error("writeFile failed");
1715                message = "Unable to create temporary file in " +
1716                    tempFile.getParent();
1717            }
1718        }
1719        setBusy(false);
1720        if (succeeded) {
1721            saved();
1722            changeFile(destination);
1723            setLastModified(getFile().lastModified());
1724            checkCVS();
1725            final String JavaDoc encoding = destination.getEncoding();
1726            if (encoding != null)
1727                saveProperties(); // Remember encoding for next time.
1728
if (isTaggable())
1729                Editor.getTagFileManager().addToQueue(
1730                    destination.getParentFile(),
1731                    mode);
1732            Sidebar.setUpdateFlagInAllFrames(SIDEBAR_REPAINT_BUFFER_LIST);
1733        } else {
1734            // Tell user exactly what error occurred.
1735
if (message != null)
1736                MessageDialog.showMessageDialog(message, "Save As");
1737            MessageDialog.showMessageDialog("Save failed", "Save As");
1738        }
1739        return succeeded;
1740    }
1741
1742    private void saveAsFtp(final FtpFile destination)
1743    {
1744        Debug.assertTrue(SwingUtilities.isEventDispatchThread());
1745        FtpSession session = FtpSession.getSession(destination);
1746        if (session == null)
1747            return;
1748        Debug.assertTrue(session.isLocked());
1749        if (!lock()) {
1750            session.unlock();
1751            MessageDialog.showMessageDialog("Buffer is busy",
1752                getFile().netPath());
1753            return;
1754        }
1755        final Editor editor = Editor.currentEditor();
1756        editor.setWaitCursor();
1757        if (!saveToCache()) {
1758            unlock();
1759            session.unlock();
1760            editor.setDefaultCursor();
1761            String JavaDoc message = "Unable to write temporary file for " +
1762                destination.netPath();
1763            MessageDialog.showMessageDialog(message, "Save As");
1764            return;
1765        }
1766        final FtpSaveProcess saveProcess =
1767            new FtpSaveProcess(this, cache, destination, session);
1768        saveProcess.setConfirmOverwrite(true);
1769        saveProcess.setTitle("Save As");
1770        final Runnable JavaDoc successRunnable = new Runnable JavaDoc() {
1771            public void run()
1772            {
1773                saved();
1774                changeFile(destination);
1775                setListing(saveProcess.getListing());
1776                Sidebar.setUpdateFlagInAllFrames(SIDEBAR_REPAINT_BUFFER_LIST);
1777                for (EditorIterator it = new EditorIterator(); it.hasNext();) {
1778                    Editor ed = it.nextEditor();
1779                    if (ed.getBuffer() == Buffer.this)
1780                        ed.setDefaultCursor();
1781                }
1782                Sidebar.repaintBufferListInAllFrames();
1783            }
1784        };
1785        saveProcess.setSuccessRunnable(successRunnable);
1786        Debug.assertTrue(isLocked());
1787        saveProcess.start();
1788    }
1789
1790    public void saveCopy(File destination)
1791    {
1792        if (destination.isLocal())
1793            saveCopyLocal(destination);
1794        else if (destination instanceof FtpFile)
1795            saveCopyFtp((FtpFile)destination);
1796        else
1797            MessageDialog.showMessageDialog("Invalid destination", "Save Copy");
1798    }
1799
1800    private void saveCopyLocal(File destination)
1801    {
1802        boolean succeeded = false;
1803        String JavaDoc message = null;
1804        if (lineSeparator == null)
1805            lineSeparator = System.getProperty("line.separator");
1806        File tempFile = Utilities.getTempFile(destination.getParent());
1807        if (tempFile == null) {
1808            message = "Unable to create temporary file for " +
1809                destination.canonicalPath();
1810        } else {
1811            tempFile.setEncoding(destination.getEncoding());
1812            if (writeFile(tempFile)) {
1813                Log.debug("buffer written to " + tempFile.canonicalPath());
1814                if (Utilities.makeBackup(destination, false)) {
1815                    destination.delete();
1816                    if (tempFile.renameTo(destination)) {
1817                        succeeded = true;
1818                    } else {
1819                        Log.error("unable to rename " +
1820                            tempFile.canonicalPath() + " to " +
1821                            destination.canonicalPath());
1822                        message = "Unable to rename temporary file";
1823                    }
1824                } else {
1825                    Log.error("backup failed");
1826                    message = "Unable to write backup file for " +
1827                        destination.canonicalPath();
1828                }
1829            } else {
1830                Log.error("writeFile failed");
1831                message = "Unable to create temporary file in " +
1832                    tempFile.getParent();
1833            }
1834        }
1835        if (succeeded) {
1836            if (isTaggable())
1837                Editor.getTagFileManager().addToQueue(destination.getParentFile(), mode);
1838        } else {
1839            // Tell user exactly what error occurred.
1840
if (message != null)
1841                MessageDialog.showMessageDialog(message, "Save Copy");
1842            MessageDialog.showMessageDialog("Save failed", "Save Copy");
1843        }
1844    }
1845
1846    private void saveCopyFtp(FtpFile destination)
1847    {
1848        Debug.assertTrue(SwingUtilities.isEventDispatchThread());
1849        FtpSession session = FtpSession.getSession(destination);
1850        if (session == null)
1851            return;
1852        Debug.assertTrue(session.isLocked());
1853        if (!lock()) {
1854            session.unlock();
1855            MessageDialog.showMessageDialog("Buffer is busy", getFile().netPath());
1856            return;
1857        }
1858        final Editor editor = Editor.currentEditor();
1859        editor.setWaitCursor();
1860        if (!saveToCache()) {
1861            unlock();
1862            session.unlock();
1863            editor.setDefaultCursor();
1864            String JavaDoc message = "Unable to write temporary file for " + destination.netPath();
1865            MessageDialog.showMessageDialog(message, "Save Copy");
1866            return;
1867        }
1868        final Runnable JavaDoc successRunnable = new Runnable JavaDoc() {
1869            public void run()
1870            {
1871                for (EditorIterator it = new EditorIterator(); it.hasNext();) {
1872                    Editor ed = it.nextEditor();
1873                    if (ed.getBuffer() == Buffer.this)
1874                        ed.setDefaultCursor();
1875                }
1876                Sidebar.repaintBufferListInAllFrames();
1877            }
1878        };
1879        final FtpSaveProcess saveProcess =
1880            new FtpSaveProcess(this, cache, destination, session);
1881        saveProcess.setConfirmOverwrite(true);
1882        saveProcess.setTitle("Save Copy");
1883        saveProcess.setSuccessRunnable(successRunnable);
1884        Debug.assertTrue(isLocked());
1885        saveProcess.start();
1886    }
1887
1888    // Removes tabs and spaces only.
1889
public void removeTrailingWhitespace()
1890    {
1891        boolean bufferChanged = false;
1892        CompoundEdit JavaDoc compoundEdit = null;
1893        try {
1894            lockWrite();
1895        }
1896        catch (InterruptedException JavaDoc e) {
1897            Log.error(e);
1898            return;
1899        }
1900        try {
1901            for (Line line = getFirstLine(); line != null; line = line.next()) {
1902                final String JavaDoc text = line.getText();
1903                final int originalLength = text.length();
1904                int length = originalLength;
1905                for (int i = originalLength - 1; i >= 0; i--) {
1906                    char c = text.charAt(i);
1907                    if (c == ' ' || c == '\t')
1908                        --length;
1909                    else
1910                        break;
1911                }
1912                if (length != originalLength) {
1913                    if (!bufferChanged) {
1914                        // First change.
1915
compoundEdit = new CompoundEdit JavaDoc();
1916                        bufferChanged = true;
1917                    }
1918                    compoundEdit.addEdit(new UndoLineEdit(this, line));
1919                    line.setText(text.substring(0, length));
1920                }
1921            }
1922            if (bufferChanged)
1923                modified();
1924        }
1925        finally {
1926            unlockWrite();
1927        }
1928        if (compoundEdit != null && undoManager != null) {
1929            compoundEdit.end();
1930            undoManager.addEdit(compoundEdit);
1931        }
1932        if (bufferChanged) {
1933            for (EditorIterator it = new EditorIterator(); it.hasNext();) {
1934                Editor ed = it.nextEditor();
1935                if (ed.getBuffer() == this) {
1936                    if (ed.getDotOffset() > ed.getDotLine().length()) {
1937                        ed.getDot().setOffset(ed.getDotLine().length());
1938                        if (getBooleanProperty(Property.RESTRICT_CARET)) {
1939                            ed.moveCaretToDotCol();
1940                            ed.updateDotLine();
1941                        }
1942                    }
1943                    if (ed.getMark() != null) {
1944                        if (ed.getMarkOffset() > ed.getMarkLine().length())
1945                            ed.getMark().setOffset(ed.getMarkLine().length());
1946                    }
1947                    ed.repaintDisplay();
1948                }
1949            }
1950        }
1951    }
1952
1953    public synchronized void autosave()
1954    {
1955        if (autosaveEnabled && Autosave.isAutosaveEnabled())
1956            if (modCount != autosaveModCount)
1957                new Thread JavaDoc(autosaveRunnable, "autosave").start();
1958    }
1959
1960    private final Runnable JavaDoc autosaveRunnable = new Runnable JavaDoc() {
1961        public void run()
1962        {
1963            try {
1964                lockRead();
1965            }
1966            catch (InterruptedException JavaDoc e) {
1967                Log.error(e);
1968                return;
1969            }
1970            try {
1971                autosaveInternal();
1972            }
1973            finally {
1974                unlockRead();
1975            }
1976        }
1977    };
1978
1979    private void autosaveInternal()
1980    {
1981        if (autosaveFile == null) {
1982            final File autosaveDirectory = Autosave.getAutosaveDirectory();
1983            if (autosaveDirectory == null)
1984                return;
1985            autosaveFile = Utilities.getTempFile(autosaveDirectory);
1986            if (autosaveFile == null)
1987                return;
1988            // Update autosave catalog file.
1989
Autosave.put(getFile().netPath(), autosaveFile.getName());
1990            Autosave.flush();
1991        }
1992        autosaveFile.setEncoding(getFile().getEncoding());
1993        if (writeFile(autosaveFile))
1994            autosaveModCount = modCount;
1995        else
1996            Log.error("autosave writeFile failed");
1997    }
1998
1999    public void deleteAutosaveFile()
2000    {
2001        if (autosaveFile != null)
2002            autosaveFile.delete();
2003    }
2004
2005    public void setFirstLine(Line line)
2006    {
2007        if (!rwlock.isWriteLocked()) {
2008            Log.error("----- setFirstLine() called without write lock -----");
2009            Debug.dumpStack();
2010        }
2011        super.setFirstLine(line);
2012    }
2013
2014    public void modified()
2015    {
2016        if (!rwlock.isWriteLocked()) {
2017            Log.error("----- modified() called without write lock -----");
2018            Debug.dumpStack();
2019        }
2020        if (modCount > saveModCount) {
2021            // Already modified.
2022
incrementModCount();
2023        } else {
2024            // First change.
2025
setModCount(saveModCount + 1);
2026            Sidebar.setUpdateFlagInAllFrames(SIDEBAR_MODIFIED_BUFFER_COUNT);
2027            Sidebar.repaintBufferListInAllFrames();
2028        }
2029        invalidate();
2030    }
2031
2032    public void unmodified()
2033    {
2034        setModCount(0);
2035        saveModCount = 0;
2036        autosaveModCount = 0;
2037        Sidebar.setUpdateFlagInAllFrames(SIDEBAR_MODIFIED_BUFFER_COUNT);
2038        Sidebar.repaintBufferListInAllFrames();
2039    }
2040
2041    public void saved()
2042    {
2043        saveModCount = modCount;
2044        autosaveModCount = modCount;
2045        if (isNewFile()) {
2046            for (Line line = getFirstLine(); line != null; line = line.next()) {
2047                line.setOriginalText(null);
2048                line.setNew(false);
2049            }
2050            setNewFile(false);
2051        } else {
2052            for (Line line = getFirstLine(); line != null; line = line.next()) {
2053                if (line.isModified())
2054                    line.setSaved(true);
2055            }
2056            if (getBooleanProperty(Property.SHOW_CHANGE_MARKS))
2057                repaint();
2058        }
2059        backedUp = false; // Ignored for local buffers.
2060
Sidebar.setUpdateFlagInAllFrames(SIDEBAR_MODIFIED_BUFFER_COUNT);
2061        Sidebar.repaintBufferListInAllFrames();
2062        deleteAutosaveFile();
2063    }
2064
2065    public void empty()
2066    {
2067        try {
2068            lockWrite();
2069        }
2070        catch (InterruptedException JavaDoc e) {
2071            Log.debug(e);
2072            return;
2073        }
2074        try {
2075            _empty();
2076        }
2077        finally {
2078            unlockWrite();
2079        }
2080        setTags(null);
2081        // Invalidate any stored views that are referencing the old contents
2082
// of this buffer.
2083
if (lastView != null)
2084            lastView.invalidate();
2085        for (EditorIterator it = new EditorIterator(); it.hasNext();) {
2086            Editor ed = it.nextEditor();
2087            View view = ed.getView(this);
2088            if (view != null)
2089                view.invalidate();
2090            if (ed.getBuffer() == this) {
2091                ed.setDot(null);
2092                ed.setMark(null);
2093                ed.setTopLine(null);
2094            }
2095        }
2096    }
2097
2098    public void invalidate()
2099    {
2100        needsParsing = true;
2101        maxColsValid = false;
2102        setTags(null);
2103    }
2104
2105    public final View getLastView()
2106    {
2107        if (lastView != null)
2108            return (View) lastView.clone();
2109        else
2110            return null;
2111    }
2112
2113    public final void setLastView(View view)
2114    {
2115        lastView = (View) view.clone();
2116    }
2117
2118    public void saveView(Editor editor)
2119    {
2120        final View view = saveViewInternal(editor);
2121        editor.setView(this, view);
2122        setLastView(view);
2123    }
2124
2125    protected View saveViewInternal(Editor editor)
2126    {
2127        final Display display = editor.getDisplay();
2128        View view = editor.getView(this);
2129        if (view == null)
2130            view = new View();
2131        final Position dot = editor.getDot();
2132        view.dot = dot == null ? null : new Position(dot);
2133        final Position mark = editor.getMark();
2134        view.mark = mark == null ? null : new Position(mark);
2135        view.selection = editor.getSelection();
2136        view.setColumnSelection(editor.isColumnSelection());
2137        view.topLine = editor.getTopLine();
2138        if (view.topLine != null)
2139            view.topLineNumber = view.topLine.lineNumber();
2140        view.pixelsAboveTopLine = display.getPixelsAboveTopLine();
2141        view.shift = display.shift;
2142        view.caretCol = display.caretCol;
2143        view.timestamp = System.currentTimeMillis();
2144        if (view.dot == null) {
2145            view.lineNumber = 0;
2146            view.offs = 0;
2147        } else {
2148            view.lineNumber = view.dot.lineNumber();
2149            view.offs = view.dot.getOffset();
2150        }
2151        return view;
2152    }
2153
2154    public boolean needsParsing()
2155    {
2156        return needsParsing;
2157    }
2158
2159    public final void setNeedsParsing(boolean b)
2160    {
2161        needsParsing = b;
2162    }
2163
2164    public boolean isModified()
2165    {
2166        return modCount != saveModCount;
2167    }
2168
2169    public Line getLine(int lineNumber)
2170    {
2171        if (lineNumber < 0)
2172            return null;
2173        int n = 0;
2174        Line line = getFirstLine();
2175        while (line != null && n != lineNumber) {
2176            line = line.next();
2177            ++n;
2178        }
2179        return line;
2180    }
2181
2182    /**
2183     * Returns position in buffer based on original line number. Never returns
2184     * null.
2185     *
2186     * @param lineNumber the original line number to look for
2187     * @param offset the offset within the line
2188     * @return the position.
2189     */

2190    public Position findOriginal(int lineNumber, int offset)
2191    {
2192        if (offset < 0)
2193            offset = 0;
2194        Line line = getFirstLine();
2195        while (line != null && line.originalLineNumber() != lineNumber)
2196            line = line.next();
2197        if (line != null)
2198            return new Position(line, Math.min(offset, line.length()));
2199        if (line == null) {
2200            // We didn't find the exact line we were looking for. Find the
2201
// line with the next highest original line number.
2202
line = getFirstLine();
2203            while (line != null && line.originalLineNumber() < lineNumber)
2204                line = line.next();
2205        }
2206        if (line != null)
2207            return new Position(line, 0);
2208        return getEnd();
2209    }
2210
2211    // Convert position into absolute character offset from start of buffer.
2212
public int getAbsoluteOffset(Position pos)
2213    {
2214        Line targetLine = pos.getLine();
2215        int offset = 0;
2216        Line line = getFirstLine();
2217        while (line != null && line != targetLine) {
2218            offset += line.length() + 1;
2219            line = line.next();
2220        }
2221        if (line == null)
2222            return -1; // Line not in buffer.
2223
else
2224            return offset + pos.getOffset();
2225    }
2226
2227    // Convert absolute character offset from start of buffer into position.
2228
public Position getPosition(int goal)
2229    {
2230        int offset = 0;
2231        Line line = getFirstLine();
2232        while (line != null) {
2233            // Line separator always counts as 1.
2234
offset += line.length() + 1;
2235            if (offset >= goal) {
2236                if (offset == goal)
2237                    line = line.next();
2238                break;
2239            }
2240            line = line.next();
2241        }
2242        if (line == null)
2243            return null; // We hit the end of the buffer without reaching our goal.
2244
if (offset == goal)
2245            return new Position(line, 0);
2246        offset -= line.length() + 1;
2247        return new Position(line, goal - offset);
2248    }
2249
2250    private UndoManager undoManager;
2251
2252    protected void initializeUndo()
2253    {
2254        undoManager = new UndoManager();
2255    }
2256
2257    public void resetUndo()
2258    {
2259        if (supportsUndo)
2260            undoManager.discardAllEdits();
2261    }
2262
2263    public void resetRedo()
2264    {
2265    }
2266
2267    public final UndoManager getUndoManager()
2268    {
2269        return undoManager;
2270    }
2271
2272    public final void addEdit(UndoableEdit JavaDoc edit)
2273    {
2274        if (undoManager != null)
2275            undoManager.addEdit(edit);
2276    }
2277
2278    public final void addUndoBoundary()
2279    {
2280        if (undoManager != null)
2281            undoManager.addEdit(UndoBoundary.getInstance());
2282    }
2283
2284    public final void appendUndoFold(Editor editor)
2285    {
2286        if (undoManager != null)
2287            undoManager.appendUndoFold(editor);
2288    }
2289
2290    public boolean canUndo()
2291    {
2292        return (undoManager != null && undoManager.canUndo());
2293    }
2294
2295    public void undo()
2296    {
2297        if (undoManager != null) {
2298            if (needsRenumbering)
2299                renumber();
2300            try {
2301                undoManager.undo();
2302            }
2303            catch (CannotUndoException JavaDoc e) {
2304                Editor.currentEditor().status("Nothing to undo");
2305            }
2306        }
2307    }
2308
2309    public boolean canRedo()
2310    {
2311        return (undoManager != null && undoManager.canRedo());
2312    }
2313
2314    public void redo()
2315    {
2316        if (undoManager != null) {
2317            if (needsRenumbering)
2318                renumber();
2319            try {
2320                undoManager.redo();
2321            }
2322            catch (CannotRedoException JavaDoc e) {
2323                Editor.currentEditor().status("Nothing to redo");
2324            }
2325        }
2326    }
2327
2328    public CompoundEdit JavaDoc beginCompoundEdit()
2329    {
2330        if (supportsUndo) {
2331            CompoundEdit JavaDoc compoundEdit = new CompoundEdit JavaDoc();
2332            undoManager.addEdit(compoundEdit);
2333            return compoundEdit;
2334        } else
2335            return null;
2336    }
2337
2338    public void endCompoundEdit(CompoundEdit JavaDoc compoundEdit)
2339    {
2340        if (compoundEdit != null)
2341            compoundEdit.end();
2342    }
2343
2344    protected void setText(String JavaDoc text)
2345    {
2346        try {
2347            lockWrite();
2348        }
2349        catch (InterruptedException JavaDoc e) {
2350            Log.debug(e);
2351            return;
2352        }
2353        try {
2354            empty();
2355            if (text != null) {
2356                FastStringReader reader = new FastStringReader(text);
2357                String JavaDoc s;
2358                while ((s = reader.readLine()) != null)
2359                    appendLine(s);
2360            }
2361            if (getFirstLine() == null)
2362                appendLine("");
2363            renumber();
2364            invalidate();
2365            setLoaded(true);
2366        }
2367        finally {
2368            unlockWrite();
2369        }
2370    }
2371
2372    // Inserts s at pos, moves pos past s.
2373
public void insertString(Position pos, String JavaDoc s)
2374    {
2375        try {
2376            lockWrite();
2377        }
2378        catch (InterruptedException JavaDoc e) {
2379            Log.error(e);
2380            return;
2381        }
2382        try {
2383            FastStringBuffer sb = new FastStringBuffer();
2384            final int limit = s.length();
2385            boolean skipLF = false;
2386            for (int i = 0; i < limit; i++) {
2387                char c = s.charAt(i);
2388                if (c == '\r') {
2389                    if (sb.length() > 0) {
2390                        insertChars(pos, sb.toString());
2391                        sb.setLength(0);
2392                    }
2393                    insertLineSeparator(pos);
2394                    skipLF = true;
2395                } else if (c == '\n') {
2396                    if (skipLF) {
2397                        skipLF = false;
2398                    } else {
2399                        if (sb.length() > 0) {
2400                            insertChars(pos, sb.toString());
2401                            sb.setLength(0);
2402                        }
2403                        insertLineSeparator(pos);
2404                    }
2405                } else {
2406                    skipLF = false;
2407                    sb.append(c);
2408                }
2409            }
2410            if (sb.length() > 0)
2411                insertChars(pos, sb.toString());
2412        }
2413        finally {
2414            unlockWrite();
2415        }
2416    }
2417
2418    // Inserts s at pos, moves pos past s.
2419
// String must not contain a line separator!
2420
public void insertChars(Position pos, String JavaDoc s)
2421    {
2422        final int length = s.length();
2423        if (length > 0) {
2424            String JavaDoc text = pos.getLine().getText();
2425            FastStringBuffer sb = new FastStringBuffer(text.length() + length);
2426            sb.append(text.substring(0, pos.getOffset()));
2427            sb.append(s);
2428            sb.append(text.substring(pos.getOffset()));
2429            pos.getLine().setText(sb.toString());
2430            pos.skip(length);
2431            modified();
2432        }
2433    }
2434
2435    public void insertLineSeparator(Position pos)
2436    {
2437        final Line line = pos.getLine();
2438        final int offset = pos.getOffset();
2439        if (offset == 0) {
2440            final Line newLine = new TextLine("");
2441            newLine.setNew(true);
2442            // Assume that the line flags should carry over to the new line.
2443
newLine.setFlags(line.flags());
2444            final Line prevLine = line.previous();
2445            newLine.setPrevious(prevLine);
2446            if (prevLine != null) {
2447                prevLine.setNext(newLine);
2448            } else {
2449                Debug.assertTrue(line == getFirstLine());
2450                setFirstLine(newLine);
2451            }
2452            newLine.setNext(line);
2453            line.setPrevious(newLine);
2454        } else if (offset == line.length()) {
2455            final Line newLine = new TextLine("");
2456            newLine.setNew(true);
2457            // Assume that the line flags should carry over to the new line.
2458
newLine.setFlags(line.flags());
2459            final Line prevLine = line;
2460            final Line nextLine = line.next();
2461            newLine.setPrevious(prevLine);
2462            if (prevLine != null) {
2463                prevLine.setNext(newLine);
2464            } else {
2465                Debug.assertTrue(line == getFirstLine());
2466                setFirstLine(newLine);
2467            }
2468            newLine.setNext(nextLine);
2469            if (nextLine != null)
2470                nextLine.setPrevious(newLine);
2471            pos.moveTo(newLine, 0);
2472        } else {
2473            final String JavaDoc head = line.substring(0, offset);
2474            final String JavaDoc tail = line.substring(offset);
2475            line.setText(head);
2476            final Line nextLine = line.next();
2477            final Line newLine = new TextLine(tail);
2478            newLine.setNew(true);
2479            // Assume that the line flags should carry over to the new line.
2480
newLine.setFlags(line.flags());
2481            line.setNext(newLine);
2482            newLine.setPrevious(line);
2483            newLine.setNext(nextLine);
2484            if (nextLine != null)
2485                nextLine.setPrevious(newLine);
2486            pos.moveTo(newLine, 0);
2487        }
2488        needsRenumbering = true;
2489        modified();
2490        repaint();
2491    }
2492
2493    // Repaint all windows displaying this buffer.
2494
public final void repaint()
2495    {
2496        for (EditorIterator it = new EditorIterator(); it.hasNext();) {
2497            Editor ed = it.nextEditor();
2498            if (ed.getBuffer() == this)
2499                ed.repaintDisplay();
2500        }
2501    }
2502
2503    public String JavaDoc getTitle()
2504    {
2505        if (title != null)
2506            return title;
2507        final File file = getFile();
2508        if (file != null)
2509            return file.netPath();
2510        else
2511            return "";
2512    }
2513
2514    public final void setTitle(String JavaDoc s)
2515    {
2516        title = s;
2517    }
2518
2519    public String JavaDoc getFileNameForDisplay()
2520    {
2521        final File file = getFile();
2522        if (file != null)
2523            return file.isRemote() ? file.netPath() : file.canonicalPath();
2524        return "";
2525    }
2526
2527    // For the buffer list.
2528
public String JavaDoc toString()
2529    {
2530        if (title != null)
2531            return title;
2532        final File file = getFile();
2533        if (file == null) {
2534            // This case should be handled by toString() in the derived class.
2535
return null;
2536        }
2537        if (file instanceof HttpFile)
2538            return file.netPath();
2539        if (file.isRemote()) {
2540            // SSH, FTP.
2541
FastStringBuffer sb = new FastStringBuffer(file.getName());
2542            sb.append(" [");
2543            sb.append(file.netPath());
2544            sb.append(']');
2545            return sb.toString();
2546        }
2547        // Local file.
2548
if (file.isDirectory())
2549            return file.canonicalPath();
2550        return Editor.getBufferList().getUniqueName(this);
2551    }
2552
2553    // For the buffer list.
2554
public Icon JavaDoc getIcon()
2555    {
2556        if (isModified())
2557            return Utilities.getIconFromFile("modified.png");
2558        else if (isReadOnly())
2559            return Utilities.getIconFromFile("read_only.png");
2560        else
2561            return Utilities.getIconFromFile("buffer.png");
2562    }
2563
2564    public final int getCol(Position pos)
2565    {
2566        return getCol(pos.getLine(), pos.getOffset());
2567    }
2568
2569    // Returns the absolute column for a given line and offset, based on the
2570
// tab width of the buffer.
2571
public final int getCol(Line line, int offset)
2572    {
2573        return getCol(line, offset, getTabWidth());
2574    }
2575
2576    public final static int getCol(Line line, int offset, int tabWidth)
2577    {
2578        final int limit = Math.min(offset, line.length());
2579        int col = 0;
2580        for (int i = 0; i < limit; i++) {
2581            if (line.charAt(i) == '\t')
2582                col += tabWidth - col % tabWidth;
2583            else
2584                ++col;
2585        }
2586        return col;
2587    }
2588
2589    // Return existing indentation of line (number of columns).
2590
public int getIndentation(Line line)
2591    {
2592        int indent = 0;
2593        final int tabWidth = getTabWidth();
2594        final int limit = line.length();
2595        for (int i = 0; i < limit; i++) {
2596            char c = line.charAt(i);
2597            if (c == '\t')
2598                indent += tabWidth - indent % tabWidth;
2599            else if (c == ' ')
2600                ++indent;
2601            else
2602                break;
2603        }
2604        return indent;
2605    }
2606
2607    // No write locking, does not call modified().
2608
public void setIndentation(Line line, int indent)
2609    {
2610        // Skip over existing indentation.
2611
final int length = line.length();
2612        int i = 0;
2613        while (i < length && Character.isWhitespace(line.charAt(i)))
2614            ++i;
2615        if (i < length) {
2616            String JavaDoc remainder = line.substring(i);
2617            if (indent > 0) {
2618                // Put required indentation into stringbuffer.
2619
FastStringBuffer sb = getCorrectIndentationString(indent);
2620                sb.append(remainder);
2621                line.setText(sb.toString());
2622            } else
2623                line.setText(remainder);
2624        } else
2625            line.setText("");
2626    }
2627
2628    public FastStringBuffer getCorrectIndentationString(int indent)
2629    {
2630        FastStringBuffer sb = new FastStringBuffer(256);
2631        if (getUseTabs()) {
2632            final int tabWidth = getTabWidth();
2633            int col = 0;
2634            while (col + tabWidth <= indent) {
2635                sb.append('\t');
2636                col += tabWidth;
2637            }
2638            while (col < indent) {
2639                sb.append(' ');
2640                ++col;
2641            }
2642        } else
2643            sb.append(Utilities.spaces(indent));
2644        return sb;
2645    }
2646
2647    private boolean folded;
2648
2649    public final void renumber()
2650    {
2651        folded = false;
2652        lineCount = 0;
2653        visibleLineCount = 0;
2654        for (Line line = getFirstLine(); line != null; line = line.next()) {
2655            line.setLineNumber(lineCount++);
2656            if (line.isHidden())
2657                folded = true;
2658            else
2659                ++visibleLineCount;
2660        }
2661        needsRenumbering = false;
2662    }
2663
2664    protected void renumberOriginal()
2665    {
2666        folded = false;
2667        lineCount = 0;
2668        visibleLineCount = 0;
2669        for (Line line = getFirstLine(); line != null; line = line.next()) {
2670            line.setOriginalLineNumber(lineCount);
2671            line.setLineNumber(lineCount++);
2672            if (line.isHidden())
2673                folded = true;
2674            else
2675                ++visibleLineCount;
2676        }
2677        needsRenumbering = false;
2678    }
2679
2680    protected void enforceOutputLimit(Property property)
2681    {
2682        Debug.assertTrue(property != null);
2683        final int outputLimit =
2684            Editor.preferences().getIntegerProperty(property);
2685        if (outputLimit == 0 || lineCount <= outputLimit)
2686            return;
2687        try {
2688            lockWrite();
2689        }
2690        catch (InterruptedException JavaDoc e) {
2691            Log.error(e);
2692            return;
2693        }
2694        try {
2695            Position begin = new Position(getFirstLine(), 0);
2696            Line line = getFirstLine();
2697            for (int i = lineCount - outputLimit; i > 0; i--) {
2698                if (line.next() == null)
2699                    break;
2700                line = line.next();
2701            }
2702            Position end = new Position(line, 0);
2703            Region r = new Region(this, begin, end);
2704            r.delete();
2705            if (needsRenumbering)
2706                renumber();
2707            resetUndo();
2708        }
2709        finally {
2710            unlockWrite();
2711        }
2712    }
2713
2714    public void saveProperties()
2715    {
2716        if (type != TYPE_NORMAL)
2717            return;
2718        if (isUntitled)
2719            return;
2720        final File file = getFile();
2721        if (file == null)
2722            return;
2723        final String JavaDoc netPath = file.netPath();
2724        // Name must not contain any characters that can't appear in an XML
2725
// attribute value. In particular, an HTTP query might contain '&'.
2726
if (netPath.indexOf('<') >= 0 || netPath.indexOf('&') >= 0)
2727            return;
2728        FileHistoryEntry entry = new FileHistoryEntry();
2729        entry.setName(netPath);
2730        entry.setEncoding(file.getEncoding());
2731        entry.setMode(mode.toString());
2732        entry.setWhen(System.currentTimeMillis());
2733        entry.setProperties(properties);
2734        FileHistory fileHistory = FileHistory.getFileHistory();
2735        fileHistory.store(entry);
2736        fileHistory.save();
2737    }
2738
2739    public void windowClosing()
2740    {
2741    }
2742
2743    public void dispose()
2744    {
2745        if (cache != null && cache.isFile()) {
2746            // Only delete the cache file if no other buffer is using it.
2747
for (BufferIterator it = new BufferIterator(); it.hasNext();) {
2748                Buffer buf = it.nextBuffer();
2749                if (buf != this && buf.getCache() == cache)
2750                    return;
2751            }
2752            // Not in use. OK to delete.
2753
cache.delete();
2754        }
2755    }
2756
2757    protected void finalize() throws Throwable JavaDoc
2758    {
2759        try {
2760            lockWrite();
2761        }
2762        catch (InterruptedException JavaDoc e) {
2763            Log.debug(e);
2764            return;
2765        }
2766        try {
2767            empty();
2768        }
2769        finally {
2770            unlockWrite();
2771        }
2772        super.finalize();
2773    }
2774
2775    public final boolean canBeRestored()
2776    {
2777        final File file = getFile();
2778        if (file != null && file.isRemote())
2779            return false;
2780        if (this instanceof RemoteBuffer)
2781            return false;
2782        switch (type) {
2783            case TYPE_NORMAL:
2784            case TYPE_ARCHIVE:
2785            case TYPE_DIRECTORY:
2786                return true;
2787            default:
2788                return false;
2789        }
2790    }
2791
2792    public final boolean isUntitled()
2793    {
2794        return isUntitled;
2795    }
2796
2797    private boolean isTransient;
2798
2799    public boolean isTransient()
2800    {
2801        return isTransient;
2802    }
2803
2804    public final void setTransient(boolean b)
2805    {
2806        unsplitOnClose = isTransient = b;
2807    }
2808
2809    private boolean unsplitOnClose;
2810
2811    public boolean unsplitOnClose()
2812    {
2813        return unsplitOnClose;
2814    }
2815
2816    public final void setUnsplitOnClose(boolean b)
2817    {
2818        unsplitOnClose = b;
2819    }
2820
2821    // Cache for getText().
2822
private SoftReference JavaDoc srText;
2823
2824    // Never returns null.
2825
public synchronized String JavaDoc getText()
2826    {
2827        if (srText != null) {
2828            final String JavaDoc text = (String JavaDoc) srText.get();
2829            if (text != null)
2830                return text;
2831        }
2832        FastStringBuffer sb = new FastStringBuffer(16384);
2833        Line line = getFirstLine();
2834        if (line != null) {
2835            String JavaDoc text = line.getText();
2836            if (text != null)
2837                sb.append(text);
2838            line = line.next();
2839            while (line != null) {
2840                text = line.getText();
2841                if (text != null) {
2842                    sb.append('\n');
2843                    sb.append(text);
2844                }
2845                line = line.next();
2846            }
2847        }
2848        final String JavaDoc text = sb.toString();
2849        srText = new SoftReference JavaDoc(text);
2850        return text;
2851    }
2852
2853    public boolean isEmpty()
2854    {
2855        Line first = getFirstLine();
2856        if (first == null)
2857            return true;
2858        if (first.next() != null)
2859            return false;
2860        return first.length() == 0;
2861    }
2862
2863    public int getDisplayHeight()
2864    {
2865        return visibleLineCount * Display.getCharHeight();
2866    }
2867
2868    // Returns cumulative height to top of target line.
2869
public int getY(Line line)
2870    {
2871        Debug.assertTrue(!line.isHidden());
2872        if (folded) {
2873            final int charHeight = Display.getCharHeight();
2874            int y = 0;
2875            for (Line l = getFirstLine(); l != null; l = l.nextVisible()) {
2876                if (l == line)
2877                    break;
2878                else
2879                    y += charHeight;
2880            }
2881            return y;
2882        } else
2883            return line.lineNumber() * Display.getCharHeight();
2884    }
2885
2886    public int getDisplayWidth()
2887    {
2888        return Display.getGutterWidth(this) +
2889            (getMaximumColumns()+1) * Display.getCharWidth();
2890    }
2891
2892    private int maxCols = 0;
2893    private boolean maxColsValid = false;
2894
2895    public int getMaximumColumns()
2896    {
2897        if (maxCols == 0) {
2898            maxCols = calculateMaximumColumns();
2899            maxColsValid = true;
2900        }
2901        return maxCols;
2902    }
2903
2904    private int calculateMaximumColumns()
2905    {
2906        if (getModeId() == BINARY_MODE)
2907            return getFirstLine().length();
2908        final int tabWidth = getTabWidth();
2909        int max = 0;
2910        for (Line line = getFirstLine(); line != null; line = line.next()) {
2911            int cols;
2912            if ((cols = getCol(line, line.length(), tabWidth)) > max)
2913                max = cols;
2914        }
2915        return max;
2916    }
2917
2918    // Returns true if there was a change.
2919
public boolean validateMaximumColumns()
2920    {
2921        if (maxColsValid)
2922            return false;
2923        final int oldMaxCols = maxCols;
2924        maxCols = calculateMaximumColumns();
2925        maxColsValid = true;
2926        return maxCols != oldMaxCols;
2927    }
2928
2929    public void setProperty(Property property, Object JavaDoc value)
2930    {
2931        properties.setProperty(property, value);
2932    }
2933
2934    public void setProperty(Property property, boolean value)
2935    {
2936        properties.setProperty(property, value);
2937    }
2938
2939    public void setProperty(Property property, int value)
2940    {
2941        properties.setProperty(property, value);
2942    }
2943
2944    boolean setPropertyFromString(Property property, String JavaDoc value)
2945    {
2946        return properties.setPropertyFromString(property, value);
2947    }
2948
2949    boolean removeProperty(Property property)
2950    {
2951        return properties.removeProperty(property);
2952    }
2953
2954    public String JavaDoc getStringProperty(Property property)
2955    {
2956        Object JavaDoc value = properties.getProperty(property);
2957        if (value instanceof String JavaDoc)
2958            return (String JavaDoc) value;
2959        if (mode != null)
2960            return mode.getStringProperty(property);
2961        return (String JavaDoc) property.getDefaultValue();
2962    }
2963
2964    public int getIntegerProperty(Property property)
2965    {
2966        if (!property.isIntegerProperty())
2967            Debug.bug();
2968        Object JavaDoc value = properties.getProperty(property);
2969        if (value instanceof Integer JavaDoc)
2970            return ((Integer JavaDoc)value).intValue();
2971        if (mode != null)
2972            return mode.getIntegerProperty(property);
2973        return ((Integer JavaDoc)property.getDefaultValue()).intValue();
2974    }
2975
2976    public boolean getBooleanProperty(Property property)
2977    {
2978        if (!property.isBooleanProperty())
2979            Debug.bug();
2980        Object JavaDoc value = properties.getProperty(property);
2981        if (value == Boolean.TRUE)
2982            return true;
2983        if (value == Boolean.FALSE)
2984            return false;
2985        if (mode != null)
2986            return mode.getBooleanProperty(property);
2987        return ((Boolean JavaDoc)property.getDefaultValue()).booleanValue();
2988    }
2989
2990    public final int getTabWidth()
2991    {
2992        return getIntegerProperty(Property.TAB_WIDTH);
2993    }
2994
2995    public void setTabWidth(int tabWidth)
2996    {
2997        properties.setProperty(Property.TAB_WIDTH, tabWidth);
2998    }
2999
3000    public final boolean getUseTabs()
3001    {
3002        return getBooleanProperty(Property.USE_TABS);
3003    }
3004
3005    public final int getIndentSize()
3006    {
3007        return getIntegerProperty(Property.INDENT_SIZE);
3008    }
3009
3010    public final void setIndentSize(int indentSize)
3011    {
3012        properties.setProperty(Property.INDENT_SIZE, indentSize);
3013    }
3014
3015    private static final Cursor JavaDoc textCursor =
3016        Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR);
3017
3018    public Cursor JavaDoc getDefaultCursor()
3019    {
3020        return textCursor;
3021    }
3022
3023    public Cursor JavaDoc getDefaultCursor(Position pos)
3024    {
3025        if (pos == null || pos.getLine() instanceof ImageLine)
3026            return Cursor.getDefaultCursor();
3027        else
3028            return textCursor;
3029    }
3030
3031    public String JavaDoc getStatusText(Editor editor)
3032    {
3033        Debug.assertTrue(editor.getBuffer() == this);
3034        Position dot = editor.getDotCopy();
3035        if (dot == null)
3036            return null;
3037        final FastStringBuffer sb = new FastStringBuffer();
3038        if (cvsEntry != null) {
3039            sb.append("CVS ");
3040            final String JavaDoc revision = cvsEntry.getRevision();
3041            if (revision.equals("0")) {
3042                sb.append('A');
3043            } else {
3044                sb.append(revision);
3045                if (cvsEntry.getCheckoutTime() != lastModified)
3046                    sb.append(" M");
3047            }
3048            sb.append(" ");
3049        }
3050        if (Editor.preferences().getBooleanProperty(Property.STATUS_BAR_DISPLAY_LINE_SEPARATOR)) {
3051            if (lineSeparator != null) {
3052                if (lineSeparator.equals("\n"))
3053                    sb.append("LF ");
3054                else if (lineSeparator.equals("\r\n"))
3055                    sb.append("CR+LF ");
3056                else if (lineSeparator.equals("\r"))
3057                    sb.append("CR ");
3058            }
3059        }
3060        sb.append("Line ");
3061        sb.append(String.valueOf(dot.lineNumber()+1));
3062        if (Editor.preferences().getBooleanProperty(Property.STATUS_BAR_DISPLAY_LINE_COUNT)) {
3063            sb.append(" of ");
3064            sb.append(String.valueOf(getLineCount()));
3065        }
3066        sb.append(" Col ");
3067        final Display display = editor.getDisplay();
3068        sb.append(String.valueOf(display.getAbsoluteCaretCol()+1));
3069        if (getBooleanProperty(Property.WRAP))
3070            sb.append(" Wrap");
3071        if (Editor.isRecordingMacro())
3072            sb.append(" [Recording macro...]");
3073        return sb.toString();
3074    }
3075
3076    public Expansion getExpansion(Position dot)
3077    {
3078        return new Expansion(dot, mode);
3079    }
3080
3081    public void restoreView(Editor editor)
3082    {
3083        final Display display = editor.getDisplay();
3084        final View view = editor.getView(this);
3085        Debug.assertTrue(view != null);
3086        if (view != null) {
3087            if (view.getDot() == null) {
3088                Line line = getLine(view.getDotLineNumber());
3089                if (line != null) {
3090                    int offset = view.getDotOffset();
3091                    if (offset < 0)
3092                        offset = 0;
3093                    else if (offset > line.length())
3094                        offset = line.length();
3095                    view.setDot(new Position(line, offset));
3096                } else {
3097                    line = getFirstLine();
3098                    if (line != null)
3099                        view.setDot(new Position(line, 0));
3100                }
3101            }
3102            editor.setDot(view.getDot() == null ? null : new Position(view.getDot()));
3103            editor.setMark(view.getMark() == null ? null : new Position(view.getMark()));
3104            editor.setSelection(view.getSelection());
3105            editor.setColumnSelection(view.isColumnSelection());
3106            if (view.getTopLine() == null){
3107                view.topLine = getFirstLine();
3108                view.pixelsAboveTopLine = 0;
3109            }
3110            display.setTopLine(view.getTopLine());
3111            display.setPixelsAboveTopLine(view.pixelsAboveTopLine);
3112            display.setShift(view.getShift());
3113            display.setCaretCol(view.getCaretCol());
3114        }
3115    }
3116
3117    private CVSEntry cvsEntry;
3118
3119    public final CVSEntry getCVSEntry()
3120    {
3121        return cvsEntry;
3122    }
3123
3124    public final void checkCVS()
3125    {
3126        cvsEntry = CVSEntry.parseEntryForFile(getFile());
3127    }
3128
3129    public final boolean isKeyword(String JavaDoc s)
3130    {
3131        return mode.isKeyword(s);
3132    }
3133}
3134
Popular Tags