/Users/lyon/j4p/src/classUtils/pack/util/PrefixPrintWriter.java

1    package classUtils.pack.util; 
2     
3    import java.io.*; 
4    import java.util.*; 
5    import java.text.*; 
6     
7    /** 
8     * A PrintWriter that unconditionally prefixes any new line of output 
9     * with a given prefix. 
10    * <p> 
11    * The prefix may be a constant string, or computed on each invocation 
12    * if a {@link PrefixProvider PrefixProvider} object is given at construction 
13    * 
14    * @version 1.1 
15    * @author C. Sadun 
16    * 
17    */ 
18   public class PrefixPrintWriter extends PrintWriter { 
19    
20       private static char [] ls=System.getProperty("line.separator").toCharArray(); 
21       private PrefixProvider pp; 
22       private int truncatedNL; 
23       private boolean start=true; 
24       private boolean autoFlush=true; 
25    
26       private static final class TimePrefixProvider implements PrefixProvider { 
27    
28           private TimePrefixProvider() { } 
29           public String getPrefix() { 
30               return "["+ 
31                      DateFormat.getDateTimeInstance().format(new Date())+ 
32                      "] "; } 
33       } 
34    
35       /** 
36        * A prefix provider to show the current directory as a prefix. 
37        * <p> 
38        * Optionally, a certain directory can be indicated to be substituted 
39        * with a "tilde" (~) charachter in the prefix. The substitution occurs 
40        * only when the current directory is the given one ora a subdirectory of the 
41        * given one (i.e., the current directory path starts or equals the the given 
42        * one's path). 
43        * 
44        */ 
45       public static final class DirectoryPrefixProvider implements PrefixProvider { 
46    
47           private String tildeValue; 
48    
49           /** 
50            * Create a prefix provider which shows the absolute path name 
51            * of the current directory 
52            */ 
53           public DirectoryPrefixProvider() { 
54               this(null); 
55           } 
56    
57           /** 
58            * Create a prefix provider which shows the absolute path name 
59            * of the current directory, substituting the given directory 
60            * with a 'tilde' (~) charachter. 
61            * @param tildeValue the directory that must be substituted with a tilde 
62            * @exception RuntimeException if the given File is not a directory 
63            */ 
64            public DirectoryPrefixProvider(File tildeValue) { 
65               if (tildeValue != null) { 
66                   if (! tildeValue.isDirectory()) 
67                       throw new RuntimeException("tilde value can only be a directory, or null"); 
68                   this.tildeValue=PathNormalizer.normalize(tildeValue).getAbsolutePath(); 
69               } 
70           } 
71    
72           /** 
73            * Return a string to prefix to the stream lines. 
74            * @return a string to prefix to the stream lines. 
75            */ 
76           public String getPrefix() { 
77               String dir = PathNormalizer.normalize(new File(".")).getAbsolutePath(); 
78               if (tildeValue != null) { 
79                   if (dir.equals(tildeValue)) dir="~"+File.separator; 
80                   else if (dir.startsWith(tildeValue)) dir="~"+File.separator+dir.substring(tildeValue.length()); 
81               } 
82               return dir; 
83           } 
84    
85       } 
86    
87       /** 
88        * A built-in provider which prefixes the output with time information 
89        */ 
90       public static final TimePrefixProvider TIME_PREFIXPROVIDER = new TimePrefixProvider(); 
91    
92       // A utility provider to allow for static strings 
93       private static class ConstantStringPrefixProvider implements PrefixProvider { 
94    
95           private String s; 
96    
97           public ConstantStringPrefixProvider(String s) { 
98               this.s=s; 
99           } 
100   
101          public String getPrefix() { return s; } 
102      } 
103   
104      /** 
105       * Build a PrefixPrintWriter which wraps the given writer, 
106       * and use the given {@link PrefixProvider PrefixProvider} 
107       * to determine the prefix. 
108       * 
109       * @param w the writer to wrap on 
110       * @param pp the {@link PrefixProvider PrefixProvider} providing the prefix for each line 
111       */ 
112      public PrefixPrintWriter(Writer w, PrefixProvider pp) { 
113          super(w, true); 
114          this.truncatedNL=0; 
115          this.pp=pp; 
116      } 
117   
118      /** 
119       * Build a PrefixPrintWriter which wraps the given writer, 
120       * and prefixes lines with the given constant string 
121       * 
122       * @param w the writer to wrap on 
123       * @param prefix the prefix for each line 
124       */ 
125      public PrefixPrintWriter(Writer w, String prefix) { 
126          this(w, new ConstantStringPrefixProvider(prefix)); 
127      } 
128   
129      /** 
130       * Build a PrefixPrintWriter which wraps the given output stream, 
131       * and use the given {@link PrefixProvider PrefixProvider} 
132       * to determine the prefix. 
133       * 
134       * @param os the output stream to wrap on 
135       * @param pp the {@link PrefixProvider PrefixProvider} providing the prefix for each line 
136       */ 
137      public PrefixPrintWriter(OutputStream os, PrefixProvider pp) { 
138          this(new OutputStreamWriter(os), pp); 
139      } 
140   
141      /** 
142       * Build a PrefixPrintWriter which wraps the given output stream, 
143       * and prefixes lines with the given constant string 
144       * 
145       * @param os the output stream to wrap on 
146       * @param prefix the prefix for each line 
147       */ 
148      public PrefixPrintWriter(OutputStream os, String prefix) { 
149          this(new OutputStreamWriter(os), prefix); 
150      } 
151   
152      /** 
153       * Write a character 
154       */ 
155      public void write(int c) { 
156       write(new char[] { (char)c }, 0, 1); 
157      } 
158   
159      /** 
160      * Write a substring of a string, of given length from a given offset 
161      */ 
162     public void write(String s, int off, int len) { 
163       write(s.toCharArray(), off, len); 
164     } 
165   
166     /** 
167      * Write a portion of character array, of given length from a given offset 
168      */ 
169     public void write(char buf[], int off, int len) { 
170      synchronized(out) { 
171   
172       String prefix = pp.getPrefix(); 
173   
174       if (start) { 
175          super.write(prefix, 0, prefix.length()); 
176          start=false; 
177       } 
178   
179       List pos = new ArrayList(); 
180       int truncated=truncatedNL; // Remember if we start in a truncated-newline situation 
181       for(int i=off;i<off+len;i++) { 
182          if (isNL(buf, i, off+len)) pos.add(new Integer(i)); 
183       } 
184       int p1 = 0; 
185       String s; 
186       for(Iterator i=pos.iterator();i.hasNext(); ) { 
187         int p2 = ((Integer)i.next()).intValue(); 
188         super.write(buf, p1, p2-p1); 
189         super.write(ls, 0, ls.length); 
190         prefix = pp.getPrefix(); 
191         super.write(prefix, 0, prefix.length()); 
192         p1=p2+ls.length-truncated; 
193         if (truncated!=0) truncated=0; // Just the first time 
194       } 
195       super.write(buf, p1, off+len-p1); 
196       if (autoFlush) super.flush(); 
197      } 
198     } 
199   
200     /** 
201      * Checks if buf matches the line separator this.ls, 
202      * setting this.truncatedNL if a partial match exists 
203      * but the buffer portion is too short for a complete 
204      * match 
205      */ 
206     private boolean isNL(char []buf, int start, int end) { 
207     for(int i=truncatedNL;i<ls.length && i<end;i++) { 
208          if (buf[start+i-truncatedNL]!=ls[i]) { 
209              if (truncatedNL !=0) truncatedNL=0; 
210              return false; 
211          } 
212      } 
213      if (end-start+truncatedNL < ls.length) { 
214          truncatedNL=end-start; 
215          return false; 
216      } 
217      if (truncatedNL !=0) truncatedNL=0; 
218      return true; 
219     } 
220   
221     /** 
222      * Terminate the current line by writing the line separator string.  The 
223      * line separator string is defined by the system property 
224      * <code>line.separator</code>, and is not necessarily a single newline 
225      * character (<code>'\n'</code>). 
226      */ 
227     public void println() { 
228      super.println(); 
229      start=true; 
230     } 
231   
232   
233     public static void main(String []args) throws Exception { 
234       PrefixPrintWriter pw = new PrefixPrintWriter(System.out, ">"); 
235       pw.write(ls[0]); 
236       pw.write(ls[1]); 
237       pw.print("This is a test... "); 
238       pw.println("Hello"+System.getProperty("line.separator")+"World"); 
239     } 
240   
241  }