KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > core > output2 > OutWriter


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 /*
20  * OutWriter.java
21  *
22  * Created on February 27, 2004, 7:24 PM
23  */

24
25 package org.netbeans.core.output2;
26
27 import java.util.logging.Logger JavaDoc;
28 import org.openide.util.NbBundle;
29 import org.openide.windows.OutputListener;
30 import java.io.*;
31 import java.nio.ByteBuffer JavaDoc;
32 import java.util.StringTokenizer JavaDoc;
33 import org.openide.util.Exceptions;
34 import org.openide.util.Utilities;
35
36 /**
37  * Implementation of OutputWriter backed by an implementation of Storage (memory mapped file, heap array, etc).
38  *
39  * @author Tim Boudreau
40  */

41 class OutWriter extends PrintWriter {
42     /** A flag indicating an io exception occured */
43     private boolean trouble = false;
44
45     private NbIO owner;
46     
47     private boolean disposed = false;
48
49     //IZ 44375 - Memory mapping fails with bad file handle on win 98
50
private static final boolean USE_HEAP_STORAGE =
51         Boolean.getBoolean("nb.output.heap") || Utilities.getOperatingSystem() == //NOI18N
52
Utilities.OS_WIN98 ||
53         Utilities.getOperatingSystem() == Utilities.OS_WIN95;
54
55     /**
56      * Byte array used to write the line separator after line writes.
57      */

58     static byte[] lineSepBytes;
59     static {
60         if (Utilities.isWindows()) {
61             lineSepBytes = new byte[] { '\0', '\r', '\0', '\n'};
62         } else {
63             lineSepBytes = new byte[] { '\0', '\n'};
64         }
65     }
66     /** The read-write backing storage. May be heap or */
67     private Storage storage;
68     
69     /** The Lines object that will be used for reading data out of the
70      * storage */

71     private AbstractLines lines = new LinesImpl();
72     
73     /** Flag set if one of several exceptions occurred while writing which
74      * mean the process doing the writing was brutally terminated. Data will
75      * be readable but not writable if set to true */

76     private boolean terminated = false;
77     
78     /** Flag set if a write failed due to disk space limits. Subsequent
79      * instances will use HeapStorage in this case */

80     static boolean lowDiskSpace = false;
81
82     
83     /**
84      * Need to remember the line start and lenght over multiple calls to
85      * write(ByteBuffer), needed to facilitate other calls than println()
86      */

87     private int lineStart;
88     private int lineLength;
89
90     /** Creates a new instance of OutWriter */
91     OutWriter(NbIO owner) {
92         this();
93         this.owner = owner;
94     }
95
96     /**
97      * Package constructor for unit tests
98      */

99     OutWriter() {
100         super (new DummyWriter());
101         lineStart = -1;
102         lineLength = 0;
103     }
104
105     Storage getStorage() {
106         if (disposed) {
107             throw new IllegalStateException JavaDoc ("Output file has been disposed!");
108         }
109         if (storage == null) {
110             storage = USE_HEAP_STORAGE || lowDiskSpace ?
111                 (Storage)new HeapStorage() : (Storage)new FileMapStorage();
112         }
113         return storage;
114     }
115     
116     boolean hasStorage() {
117         return storage != null;
118     }
119     
120     boolean isDisposed() {
121         return disposed;
122     }
123     
124     boolean isEmpty() {
125         return storage == null ? true : lines == null ? true :
126             lines.getLineCount() == 0;
127     }
128
129     public String JavaDoc toString() {
130         return "OutWriter@" + System.identityHashCode(this) + " for " + owner + " closed ";
131     }
132
133     private int doPrintln (String JavaDoc s) {
134         try {
135             int idx = s.indexOf("\n");
136             int result = 1;
137             if (idx != -1) { //XXX platform specific line sep?
138
//XXX this can be much more efficient by slicing buffers
139
StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(s, "\n", true); //NOI18N
140
result = 0;
141                 boolean lastWasNewLine = true;
142                 while (tok.hasMoreTokens()) {
143                     String JavaDoc token = tok.nextToken();
144                     if ("\n".equals(token)) {
145                         if (lastWasNewLine) {
146                             doPrintln("");
147                             result++;
148                         }
149                         lastWasNewLine = true;
150                     } else {
151                         lastWasNewLine = false;
152                         doPrintln(token);
153                         result++;
154                     }
155                 }
156             } else {
157                 ByteBuffer JavaDoc buf;
158                 synchronized (this) {
159                     if (s.startsWith("\t")) { //NOI18N
160
char[] c = s.toCharArray();
161                         int ix = 0;
162                         //Temporary handling of leading tab characters, so they
163
//at least have some width. Note this does affect output
164
//written with save-as
165
StringBuffer JavaDoc sb = new StringBuffer JavaDoc (s.length() + 10);
166                         for (int i=0; i < c.length; i++) {
167                             if ('\t' == c[i]) {
168                                 sb.append(" "); // NOI18N
169
} else {
170                                 sb.append (c[i]);
171                             }
172                         }
173                         s = sb.toString();
174                     }
175                     buf = getStorage().getWriteBuffer(AbstractLines.toByteIndex(s.length()));
176                     buf.asCharBuffer().put(s);
177                     buf.position (buf.position() + AbstractLines.toByteIndex(s.length()));
178                     write (buf, true);
179                 }
180             }
181             return result;
182         } catch (IOException ioe) {
183             handleException (ioe);
184             return 0;
185         }
186     }
187
188
189     /** Generic exception handling, marking the error flag and notifying it with ErrorManager */
190     private void handleException (Exception JavaDoc e) {
191         setError();
192         String JavaDoc msg = Exceptions.findLocalizedMessage(e);
193         if (msg == null) {
194             Exceptions.attachLocalizedMessage(e,
195                                               NbBundle.getMessage(OutWriter.class,
196                                                                   "MSG_GenericError")); //NOI18N
197
}
198         if (Controller.LOG) {
199             StackTraceElement JavaDoc[] el = e.getStackTrace();
200             Controller.log ("EXCEPTION: " + e.getClass() + e.getMessage());
201             for (int i=1; i < el.length; i++) {
202                 Controller.log (el[i].toString());
203             }
204         }
205         Exceptions.printStackTrace(e);
206     }
207
208     /**
209      * Write the passed buffer to the backing storage, recording the line start in the mapping of lines to
210      * byte offsets.
211      *
212      * @param bb
213      * @throws IOException
214      */

215     public synchronized void write(ByteBuffer JavaDoc bb, boolean completeLine) throws IOException {
216         if (checkError() || terminated) {
217             return;
218         }
219         lines.markDirty();
220         int length = bb.limit();
221         closed = false;
222         int start = -1;
223         try {
224             start = getStorage().write(bb, completeLine);
225         } catch (java.nio.channels.ClosedByInterruptException JavaDoc cbie) {
226             //Execution termination has sent ThreadDeath to the process in the
227
//middle of a write
228
threadDeathClose();
229         } catch (java.nio.channels.AsynchronousCloseException JavaDoc ace) {
230             //Execution termination has sent ThreadDeath to the process in the
231
//middle of a write
232
threadDeathClose();
233         } catch (IOException ioe) {
234             //Out of disk space
235
if (ioe.getMessage().indexOf("There is not enough space on the disk") != -1) { //NOI18N
236
lowDiskSpace = true;
237                 String JavaDoc msg = NbBundle.getMessage(OutWriter.class,
238                     "MSG_DiskSpace", storage); //NOI18N
239
Exceptions.attachLocalizedMessage(ioe, msg);
240                 Exceptions.printStackTrace(ioe);
241                 setError();
242                 storage.dispose();
243             } else {
244                 //Existing output may still be readable - close, but leave
245
//open for reads - if there's a problem there too, the error
246
//flag will be set when a read is attempted
247
Exceptions.printStackTrace(ioe);
248                 threadDeathClose();
249             }
250         }
251         boolean startedNow = false;
252         if (start >= 0 && lineStart == -1) {
253             lineStart = start;
254             lineLength = lineLength + length;
255             startedNow = true;
256         }
257         if (completeLine) {
258             if (lineStart >= 0 && !terminated && lines != null) {
259                 if (Controller.VERBOSE) Controller.log (this + ": Wrote " +
260                         ((ByteBuffer JavaDoc)bb.flip()).asCharBuffer() + " at " + start);
261                 if (startedNow) {
262                     lines.lineWritten (lineStart, lineLength);
263                 } else {
264                     lines.lineFinished(lineLength);
265                 }
266                 lineStart = -1;
267                 lineLength = 0;
268                 if (owner != null && owner.hasStreamClosed()) {
269                     owner.setStreamClosed(false);
270                     lines.fire();
271                 }
272             }
273         } else {
274             if (startedNow && lineStart >= 0 && !terminated && lines != null) {
275                 lines.lineStarted(lineStart);
276                 if (owner != null && owner.hasStreamClosed()) {
277                     owner.setStreamClosed(false);
278                     lines.fire();
279                 }
280             }
281         }
282     }
283
284     /**
285      * An exception has occurred while writing, which has left us in a readable state, but not
286      * a writable one. Typically this happens when an executing process was sent Thread.stop()
287      * in the middle of a write.
288      */

289     void threadDeathClose() {
290         terminated = true;
291         if (Controller.LOG) Controller.log (this + " Close due to termination");
292         ErrWriter err = owner.writer().err();
293         if (err != null) {
294             err.closed=true;
295         }
296         owner.setStreamClosed(true);
297         close();
298     }
299
300     /**
301      * Dispose this writer. If reuse is true, the underlying storage will be disposed, but the
302      * OutWriter will still be usable. If reuse if false, note that any current ChangeListener is cleared.
303      *
304      */

305     public synchronized void dispose() {
306         if (disposed) {
307             //This can happen if a tab was closed, so we were already disposed, but then the
308
//ant module tries to reuse the tab -
309
return;
310         }
311         if (Controller.LOG) Controller.log (this + ": OutWriter.dispose - owner is " + (owner == null ? "null" : owner.getName()));
312         clearListeners();
313         if (storage != null) {
314             storage.dispose();
315             storage = null;
316         }
317         if (lines != null) {
318             lines.clear();
319         }
320         trouble = true;
321         if (Controller.LOG) Controller.log (this + ": Setting owner to null, trouble to true, dirty to false. This OutWriter is officially dead.");
322         owner = null;
323         disposed = true;
324     }
325
326
327     private void clearListeners() {
328         if (Controller.LOG) Controller.log (this + ": Sending outputLineCleared to all listeners");
329         if (owner == null) {
330             //Somebody called reset() twice
331
return;
332         }
333         synchronized (this) {
334             if (lines != null && lines.hasHyperlinks()) {
335                 int[] listenerLines = lines.allListenerLines();
336                 Controller.ControllerOutputEvent e = new Controller.ControllerOutputEvent(owner, 0);
337                 for (int i=0; i < listenerLines.length; i++) {
338                     OutputListener ol = (OutputListener) lines.getListenerForLine(listenerLines[i]);
339                     if (Controller.LOG) {
340                         Controller.log("Clearing listener " + ol);
341                     }
342                     e.setLine(listenerLines[i]);
343                     if (ol != null) {
344                         ol.outputLineCleared(e);
345                     } else {
346                         //#56826 - debug messaging
347
Logger.getAnonymousLogger().warning("issue #56826 - There was a null OutputListener on line:" + listenerLines[i]);
348                     }
349                 }
350             } else {
351                 if (Controller.LOG) Controller.log (this + ": No listeners to clear");
352             }
353         }
354     }
355
356     public synchronized boolean isClosed() {
357         if (checkError() || storage == null || storage.isClosed()) {
358             return true;
359         } else {
360             return closed;
361         }
362     }
363
364     public Lines getLines() {
365         return lines;
366     }
367
368     private boolean closed = false;
369     public synchronized void close() {
370         closed = true;
371         try {
372             //#49955 - possible (but difficult) to end up with close()
373
//called twice
374
if (storage != null) {
375                 storage.close();
376             }
377             if (lines != null) {
378                 lines.fire();
379             }
380         } catch (IOException ioe) {
381             handleException (ioe);
382         }
383     }
384
385        public synchronized void println(String JavaDoc s) {
386             if (checkError()) {
387                 return;
388             }
389             doPrintln(s);
390         }
391
392         public synchronized void flush() {
393             if (checkError()) {
394                 return;
395             }
396             try {
397                 getStorage().flush();
398                 if (lines != null) {
399                     lines.fire();
400                 }
401             } catch (IOException e) {
402                 handleException (e);
403             }
404         }
405
406
407         public boolean checkError() {
408             return disposed || trouble;
409         }
410
411         public synchronized void write(int c) {
412             if (checkError()) {
413                 return;
414             }
415             try {
416                 ByteBuffer JavaDoc buf = getStorage().getWriteBuffer(AbstractLines.toByteIndex(1));
417                 buf.asCharBuffer().put((char)c);
418                 buf.position (buf.position() + AbstractLines.toByteIndex(1));
419                 write (buf, false);
420             } catch (IOException ioe) {
421                 handleException (ioe);
422             }
423         }
424
425         public synchronized void write(char data[], int off, int len) {
426             if (checkError()) {
427                 return;
428             }
429             if (len > 0 && data[off] == '\t') {
430                 //Temporary handling of leading tab characters, so they
431
//at least have some width. Note this does affect output
432
//written with save-as
433
StringBuffer JavaDoc sb = new StringBuffer JavaDoc(data.length + 10);
434                 int cnt = 0;
435                 for (int i=0; i < data.length; i++) {
436                     if (data[i] == '\t') {
437                         sb.append(" "); // NOI18N
438
cnt = cnt + 1;
439                     } else {
440                         sb.append (data[i]);
441                     }
442                 }
443                 data = sb.toString().toCharArray();
444                 len = len + (cnt * 7);
445             }
446             
447             int count = off;
448             int start = off;
449             while (count < len + off) {
450                 char curr = data[count];
451                 if (curr == '\n') { //NOI18N
452
//TODO we can optimize the array writing a bit by not delegating to the
453
//println metod which perform a physical write on each line,
454
// but to write just once, when everything is processed.
455
String JavaDoc sx = new String JavaDoc(data, start, (count + 1 - start));
456                     println (sx);
457                     start = count + 1;
458                     if (start >= len + off) {
459                         return;
460                     }
461                 }
462                 count++;
463             }
464             try {
465                 synchronized (this) {
466                     int lenght = count - (start);
467                     ByteBuffer JavaDoc buf = getStorage().getWriteBuffer(AbstractLines.toByteIndex(lenght));
468                     buf.asCharBuffer().put(data, start, lenght);
469                     buf.position(buf.position() + AbstractLines.toByteIndex(lenght));
470                     write(buf, false);
471                 }
472             } catch (IOException ioe) {
473                 handleException(ioe);
474                 return;
475             }
476         }
477
478         public synchronized void write(char data[]) {
479             write (data, 0, data.length);
480         }
481         
482         public synchronized void println() {
483             doPrintln("");
484         }
485
486         /**
487          * Write a portion of a string.
488          * @param s A String
489          * @param off Offset from which to start writing characters
490          * @param len Number of characters to write
491          */

492         public synchronized void write(String JavaDoc s, int off, int len) {
493             write (s.toCharArray(), off, len);
494         }
495
496         public synchronized void write(String JavaDoc s) {
497             write (s.toCharArray());
498         }
499
500
501         public synchronized void println(String JavaDoc s, OutputListener l) throws IOException {
502             println(s, l, false);
503         }
504
505         
506         public synchronized void println(String JavaDoc s, OutputListener l, boolean important) throws IOException {
507             if (checkError()) {
508                 return;
509             }
510             int addedCount = doPrintln (s);
511             int newCount = lines.getLineCount();
512             for (int i=newCount - addedCount; i < newCount; i++) {
513                 lines.addListener (i, l, important);
514                 //#48485 we should update the UI, since the lines are in the model
515
// and jump next/previous can't jump to appropriate place.
516
lines.fire();
517             }
518         }
519         
520     /**
521      * A useless writer object to pass to the superclass constructor. We override all methods
522      * of it anyway.
523      */

524     static class DummyWriter extends Writer {
525         
526         DummyWriter() {
527             super (new Object JavaDoc());
528         }
529         
530         public void close() throws IOException {
531         }
532         
533         public void flush() throws IOException {
534         }
535         
536         public void write(char[] cbuf, int off, int len) throws IOException {
537         }
538     }
539
540     private class LinesImpl extends AbstractLines {
541         LinesImpl() {
542             super();
543         }
544
545         protected Storage getStorage() {
546             return OutWriter.this.getStorage();
547         }
548
549         protected boolean isDisposed() {
550              return OutWriter.this.disposed;
551         }
552
553         protected boolean isTrouble() {
554             return OutWriter.this.trouble;
555         }
556
557         public Object JavaDoc readLock() {
558             return OutWriter.this;
559         }
560
561         public boolean isGrowing() {
562             return !isClosed();
563         }
564
565         protected void handleException (Exception JavaDoc e) {
566             OutWriter.this.handleException(e);
567         }
568     }
569 }
570
Popular Tags