KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * SystemBuffer.java
3  *
4  * Copyright (C) 1998-2003 Peter Graves
5  * $Id: SystemBuffer.java,v 1.15 2003/06/14 17:49:03 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.io.BufferedOutputStream JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.io.UnsupportedEncodingException JavaDoc;
28 import java.util.List JavaDoc;
29
30 // System buffers are NOT linked into the normal buffer ring.
31
public class SystemBuffer implements Constants
32 {
33     public static final int TYPE_SYSTEM = 0;
34     public static final int TYPE_NORMAL = 1;
35     public static final int TYPE_ARCHIVE = 2;
36     public static final int TYPE_DIRECTORY = 3;
37     public static final int TYPE_SHELL = 4;
38     public static final int TYPE_MAN = 5;
39     public static final int TYPE_OUTPUT = 6;
40     public static final int TYPE_IMAGE = 7;
41     public static final int TYPE_MAILBOX = 8;
42     public static final int TYPE_TELNET = 9;
43     public static final int TYPE_SSH = 10;
44     public static final int TYPE_LIST_OCCURRENCES = 11;
45
46     protected int type = TYPE_SYSTEM;
47     protected boolean readOnly;
48     protected boolean forceReadOnly;
49     protected Mode mode;
50     protected String JavaDoc lineSeparator;
51     protected int lineCount;
52
53     private boolean isLoaded;
54     private Line firstLine;
55     private Line lastLine;
56     private File file;
57     private String JavaDoc loadEncoding;
58     private List JavaDoc tags;
59
60     public SystemBuffer()
61     {
62     }
63
64     public SystemBuffer(File file)
65     {
66         this.file = file;
67     }
68
69     public final int getType()
70     {
71         return type;
72     }
73
74     public final synchronized Line getFirstLine()
75     {
76         return firstLine;
77     }
78
79     public synchronized void setFirstLine(Line line)
80     {
81         firstLine = line;
82     }
83
84     public final Position getEnd()
85     {
86         Line line = firstLine;
87         if (line == null)
88             return null;
89         while (line.next() != null)
90             line = line.next();
91         return new Position(line, line.length());
92     }
93
94     public final File getFile()
95     {
96         return file;
97     }
98
99     public final void setFile(File file)
100     {
101         this.file = file;
102     }
103
104     public final synchronized boolean isLoaded()
105     {
106         return isLoaded;
107     }
108
109     public final synchronized void setLoaded(boolean b)
110     {
111         isLoaded = b;
112     }
113
114     public final Mode getMode()
115     {
116         return mode;
117     }
118
119     public final int getModeId()
120     {
121         return mode == null ? 0 : mode.getId();
122     }
123
124     public final String JavaDoc getModeName()
125     {
126         return mode == null ? null : mode.toString();
127     }
128
129     public synchronized final List JavaDoc getTags()
130     {
131         return tags;
132     }
133
134     public synchronized final void setTags(List JavaDoc tags)
135     {
136         this.tags = tags;
137     }
138
139     public final void setForceReadOnly(boolean b)
140     {
141         forceReadOnly = b;
142     }
143
144     public String JavaDoc getLineSeparator()
145     {
146         return lineSeparator;
147     }
148
149     public final boolean contains(Line line)
150     {
151         Line l = getFirstLine();
152         while (l != null) {
153             if (l == line)
154                 return true;
155             l = l.next();
156         }
157         return false;
158     }
159
160     public int load()
161     {
162         if (!isLoaded) {
163             try {
164                 if (file.isFile()) {
165                     InputStream JavaDoc in = file.getInputStream();
166                     if (in != null) {
167                         load(in, file.getEncoding());
168                         in.close();
169                     }
170                 }
171                 if (getFirstLine() == null) {
172                     // New or 0-byte file.
173
appendLine("");
174                     lineSeparator = System.getProperty("line.separator");
175                 }
176             }
177             catch (IOException JavaDoc e) {
178                 Log.error(e);
179             }
180             isLoaded = true;
181         }
182         return LOAD_COMPLETED;
183     }
184
185     public void load(InputStream JavaDoc istream, String JavaDoc encoding)
186     {
187         if (mode != null && mode.getId() == BINARY_MODE) {
188             loadBinary(istream);
189             return;
190         }
191         byte[] buf = new byte[4096];
192         int totalBytes = 0;
193         try {
194             int bytesRead = istream.read(buf);
195             loadProgress(totalBytes = totalBytes + bytesRead);
196             // Detect Unicode.
197
boolean isUnicode = false;
198             boolean isLittleEndian = true;
199             if (bytesRead >= 2) {
200                 byte byte1 = buf[0];
201                 byte byte2 = buf[1];
202                 if (byte1 == (byte) 0xfe && byte2 == (byte) 0xff) {
203                     isUnicode = true;
204                     isLittleEndian = false;
205                     loadEncoding = "UnicodeBig";
206                 } else if (byte1 == (byte) 0xff && byte2 == (byte) 0xfe) {
207                     isUnicode = true;
208                     loadEncoding = "UnicodeLittle";
209                 }
210             }
211             boolean skipLF = false;
212             if (isUnicode) {
213                 FastStringBuffer sb = new FastStringBuffer(256);
214                 int i = 2;
215                 while (bytesRead > 0) {
216                     while (i < bytesRead - 1) {
217                         char c;
218                         final byte b1 = buf[i++];
219                         final byte b2 = buf[i++];
220                         if (isLittleEndian)
221                             c = (char) ((b2 << 8) + (b1 & 0xff));
222                         else
223                             c = (char) ((b1 << 8) + (b2 & 0xff));
224                         switch (c) {
225                             case '\r':
226                                 appendLine(sb.toString());
227                                 sb.setLength(0);
228                                 skipLF = true;
229                                 break;
230                             case '\n':
231                                 if (skipLF) {
232                                     // LF after CR.
233
if (lineSeparator == null)
234                                         lineSeparator = "\r\n";
235                                     skipLF = false;
236                                 } else {
237                                     // LF without preceding CR.
238
if (lineSeparator == null)
239                                         lineSeparator = "\n";
240                                     appendLine(sb.toString());
241                                     sb.setLength(0);
242                                 }
243                                 break;
244                             default:
245                                 // Normal char.
246
if (skipLF) {
247                                     // Something other than LF after CR. Must be a Mac...
248
if (lineSeparator == null)
249                                         lineSeparator = "\r";
250                                     skipLF = false;
251                                 }
252                                 sb.append(c);
253                                 break;
254                         }
255                     }
256                     bytesRead = istream.read(buf);
257                     i = 0;
258                 }
259                 if (sb.length() > 0) {
260                     // No line separator at end of file.
261
appendLine(sb.toString());
262                 } else {
263                     // If there is a line separator at the end of the file, we
264
// need to append an empty line so the line separator will
265
// get written out when the file is saved.
266
appendLine("");
267                 }
268             } else {
269                 // Not Unicode.
270
if (encoding == null) {
271                     encoding =
272                         Editor.preferences().getStringProperty(Property.DEFAULT_ENCODING);
273                 }
274                 loadEncoding = encoding;
275                 ByteBuffer bb = new ByteBuffer(256);
276                 while (bytesRead > 0) {
277                     for (int i = 0; i < bytesRead; i++) {
278                         byte b = buf[i];
279                         switch (b) {
280                             case 13:
281                                 appendLine(new String JavaDoc(bb.getBytes(), 0, bb.length(), encoding));
282                                 bb.setLength(0);
283                                 skipLF = true;
284                                 break;
285                             case 10:
286                                 if (skipLF) {
287                                     // LF after CR.
288
if (lineSeparator == null)
289                                         lineSeparator = "\r\n";
290                                     skipLF = false;
291                                 } else {
292                                     // LF without preceding CR.
293
if (lineSeparator == null)
294                                         lineSeparator = "\n";
295                                     appendLine(new String JavaDoc(bb.getBytes(), 0, bb.length(), encoding));
296                                     bb.setLength(0);
297                                 }
298                                 break;
299                             default:
300                                 // Normal char.
301
if (skipLF) {
302                                     // Something other than LF after CR. Must be a Mac...
303
if (lineSeparator == null)
304                                         lineSeparator = "\r";
305                                     skipLF = false;
306                                 }
307                                 bb.append(b);
308                                 break;
309                         }
310                     }
311                     bytesRead = istream.read(buf);
312                     if (bytesRead > 0)
313                         loadProgress(totalBytes = totalBytes + bytesRead);
314                 }
315                 if (bb.length() > 0) {
316                     // No line separator at end of file.
317
appendLine(new String JavaDoc(bb.getBytes(), 0, bb.length(), encoding));
318                 } else {
319                     // If there is a line separator at the end of the file, we
320
// need to append an empty line so the line separator will
321
// get written out when the file is saved.
322
appendLine("");
323                 }
324             }
325             isLoaded = true;
326         }
327         catch (Exception JavaDoc e) {
328             Log.error(e);
329         }
330         loadFinished(isLoaded);
331     }
332
333     public final Line getLastLine()
334     {
335         return lastLine;
336     }
337
338     public final void setLastLine(Line line)
339     {
340         lastLine = line;
341     }
342
343     protected void appendLine(Line line)
344     {
345         line.setPrevious(lastLine);
346         if (lastLine != null)
347             lastLine.setNext(line);
348         lastLine = line;
349         if (getFirstLine() == null)
350             setFirstLine(line);
351     }
352
353     public void appendLine(String JavaDoc s)
354     {
355         appendLine(new TextLine(s));
356     }
357
358     public void append(String JavaDoc s)
359     {
360         int begin = 0;
361         int end = 0;
362         boolean skipLF = false;
363         final int limit = s.length();
364         for (int i = 0; i < limit; i++) {
365             switch (s.charAt(i)) {
366                 case '\r':
367                     appendLine(s.substring(begin, end));
368                     ++end;
369                     begin = end;
370                     skipLF = true;
371                     break;
372                 case '\n':
373                     if (skipLF) {
374                         ++begin;
375                         ++end;
376                         skipLF = false;
377                     } else {
378                         appendLine(s.substring(begin, end));
379                         ++end;
380                         begin = end;
381                     }
382                     break;
383                 default:
384                     skipLF = false;
385                     ++end;
386             }
387         }
388         if (begin < end)
389             appendLine(s.substring(begin, end));
390     }
391
392     private void appendBinaryLine(int start, byte[] bytes, int numBytes)
393     {
394         appendLine(new BinaryLine(start, bytes, numBytes));
395     }
396
397     // Overridden by Buffer.renumber().
398
public void renumber()
399     {
400         for (Line line = getFirstLine(); line != null; line = line.next())
401             line.setLineNumber(lineCount++);
402     }
403
404     public void writeBuffer() throws SaveException
405     {
406         if (file.isFile() && !file.canWrite()) {
407             Log.error("writeFile: file is not writable: " + file);
408             throw new SaveException(file,
409                                     file.canonicalPath() + " is not writable");
410         }
411         if (Platform.isPlatformWindows()) {
412             // writeTemporaryFile() throws a SaveException if an error occurs.
413
File tempFile = writeTemporaryFile();
414             if (!makePatchFile()) {
415                 if (!Utilities.makeBackup(file, false)) {
416                     Log.error("backup failed");
417                     throw new SaveException(file,
418                         "Unable to write backup file for " + file.canonicalPath());
419                 }
420             }
421             if (!Utilities.deleteRename(tempFile, file)) {
422                 Log.error("unable to rename " + tempFile.canonicalPath() +
423                     " to " + file.canonicalPath());
424                 throw new SaveException(file,
425                     "Unable to rename temporary file");
426             }
427         } else {
428             // Save in place on Unix to preserve permissions and ownership of
429
// file. Keep original (instead of renaming it) when making backup.
430
if (!makePatchFile()) {
431                 if (!Utilities.makeBackup(file, true)) {
432                     Log.error("backup failed");
433                     throw new SaveException(file,
434                         "Unable to write backup file for ".concat(
435                             file.canonicalPath()));
436                 }
437             }
438             // Write directly to original file.
439
if (!writeFile(file)) {
440                 Log.error("writeFile failed");
441                 throw new SaveException(file,
442                     "Unable to write ".concat(file.canonicalPath()));
443             }
444         }
445     }
446
447     // Returns true if patch file was created successfully.
448
private final boolean makePatchFile()
449     {
450         if (file.isFile()) {
451             File patchFile = getPatchFile();
452             if (patchFile != null) {
453                 if (!patchFile.isFile())
454                     return Utilities.copyFile(file, patchFile);
455             }
456         }
457         return false;
458     }
459
460     // Returns null if "patchmode" preference is not set.
461
// Public for DiffMode.diff().
462
public final File getPatchFile()
463     {
464         String JavaDoc suffix;
465         if (this instanceof Buffer)
466             suffix = ((Buffer)this).getStringProperty(Property.PATCH_MODE);
467         else if (mode != null)
468             suffix = mode.getStringProperty(Property.PATCH_MODE);
469         else {
470             suffix =
471                 Editor.preferences().getStringProperty(Property.PATCH_MODE);
472         }
473         if (suffix != null) {
474             suffix = suffix.trim();
475             if (suffix.length() > 0) {
476                 if (suffix.charAt(0) != '.')
477                     suffix = ".".concat(suffix);
478                 return File.getInstance(file.canonicalPath().concat(suffix));
479             }
480         }
481         return null;
482     }
483
484     public boolean writeFile(File outputFile)
485     {
486         try {
487             BufferedOutputStream JavaDoc out = new BufferedOutputStream JavaDoc(outputFile.getOutputStream());
488             if (lineSeparator == null)
489                 lineSeparator = System.getProperty("line.separator");
490             String JavaDoc encoding = outputFile.getEncoding();
491             if (encoding == null)
492                 encoding = getSaveEncoding();
493             Line line = getFirstLine();
494             if (line != null) {
495                 final byte[] byteOrderMark = getByteOrderMark(encoding);
496                 if (byteOrderMark != null)
497                     out.write(byteOrderMark);
498                 out.write(line.getBytes(encoding));
499                 line = line.next();
500                 final byte[] sepBytes = getSeparatorBytes(encoding);
501                 while (line != null) {
502                     out.write(sepBytes);
503                     out.write(line.getBytes(encoding));
504                     line = line.next();
505                 }
506             }
507             out.flush();
508             out.close();
509             return true;
510         }
511         catch (IOException JavaDoc e) {
512             Log.error(e);
513             return false;
514         }
515     }
516
517     public String JavaDoc getSaveEncoding()
518     {
519         String JavaDoc encoding = file == null ? null : file.getEncoding();
520         if (encoding == null) {
521             encoding = loadEncoding;
522             if (encoding == null)
523                 encoding = Editor.preferences().getStringProperty(
524                     Property.DEFAULT_ENCODING);
525         }
526         if (encoding == null)
527             Debug.bug();
528         return encoding;
529     }
530
531     byte[] getByteOrderMark(String JavaDoc encoding) throws UnsupportedEncodingException JavaDoc
532     {
533         byte[] bytes = "test".getBytes(encoding);
534         if ((bytes[0] == (byte) 0xfe && bytes[1] == (byte) 0xff) ||
535             (bytes[0] == (byte) 0xff && bytes[1] == (byte) 0xfe)) {
536             byte[] byteOrderMark = new byte[2];
537             byteOrderMark[0] = bytes[0];
538             byteOrderMark[1] = bytes[1];
539             return byteOrderMark;
540         }
541         return null;
542     }
543
544     byte[] getSeparatorBytes(String JavaDoc encoding) throws UnsupportedEncodingException JavaDoc
545     {
546         byte[] bytes = lineSeparator.getBytes(encoding);
547         if (bytes.length > 2) {
548             if ((bytes[0] == (byte) 0xfe && bytes[1] == (byte) 0xff) ||
549                 (bytes[0] == (byte) 0xff && bytes[1] == (byte) 0xfe)) {
550                 byte[] sepBytes = new byte[bytes.length-2];
551                 for (int i = 0; i < sepBytes.length; i++)
552                     sepBytes[i] = bytes[i+2];
553                 return sepBytes;
554             }
555         }
556         return bytes;
557     }
558
559     /*package*/ synchronized void _empty()
560     {
561         Line line = getFirstLine();
562         while (line != null) {
563             Line nextLine = line.next();
564             line.setPrevious(null);
565             line.setNext(null);
566             line = nextLine;
567         }
568         setFirstLine(null);
569         lastLine = null;
570         isLoaded = false;
571     }
572
573     protected void loadProgress(int totalBytesRead)
574     {
575         // Default behavior is to do nothing.
576
}
577
578     protected void loadFinished(boolean success)
579     {
580         // Default behavior is to do nothing.
581
}
582
583     private void loadBinary(InputStream JavaDoc istream)
584     {
585         byte[] array = readAllBytes(istream);
586         if (array != null) {
587             for (int start = 0; start < array.length; start += 16) {
588                 int bytesLeft = array.length - start;
589                 int count = bytesLeft >= 16 ? 16 : bytesLeft;
590                 appendBinaryLine(start, array, count);
591             }
592             isLoaded = true;
593         }
594         loadFinished(isLoaded);
595     }
596
597     private byte[] readAllBytes(InputStream JavaDoc in)
598     {
599         final int chunkSize = 0x8000;
600         byte[] array = null;
601         int totalBytes = 0;
602         byte[] chunk = new byte[ chunkSize ];
603         int bytesRead;
604         try {
605             while ((bytesRead = in.read(chunk, 0, chunk.length)) > 0) {
606                 if (array == null) {
607                     array = new byte[bytesRead];
608                     System.arraycopy(chunk, 0, array, 0, bytesRead);
609                 } else {
610                     // Allocate new array big enough to hold all the bytes.
611
byte[] newArray = new byte[totalBytes + bytesRead];
612
613                     // Copy old array into new array.
614
if (totalBytes > 0)
615                         System.arraycopy(array, 0, newArray, 0, totalBytes);
616
617                     // Append current chunk.
618
System.arraycopy(chunk, 0, newArray, totalBytes, bytesRead);
619
620                     array = newArray;
621                 }
622                 totalBytes += bytesRead;
623                 Debug.assertTrue(array.length == totalBytes);
624             }
625         }
626         catch (IOException JavaDoc e) {
627             Log.error(e);
628             array = null;
629         }
630         return array;
631     }
632
633     private File writeTemporaryFile() throws SaveException
634     {
635         boolean succeeded = false;
636         // First try to write out a temporary file in the current directory.
637
File tempFile = Utilities.getTempFile(file.getParent());
638         if (tempFile != null)
639             succeeded = writeFile(tempFile);
640         if (!succeeded) {
641             // We were not able to write out the temporary file in the current
642
// directory, possibly because the current directory is not
643
// writable. Try again using j's temp directory.
644
tempFile = Utilities.getTempFile();
645             if (tempFile != null)
646                 succeeded = writeFile(tempFile);
647         }
648         if (!succeeded) {
649             throw new SaveException(file,
650                 "Unable to write temporary file for ".concat(
651                     file.canonicalPath()));
652         }
653         return tempFile;
654     }
655 }
656
Popular Tags