1package org.testng.reporters;
2
3import java.io.BufferedWriter;
4import java.io.File;
5import java.io.FileReader;
6import java.io.FileWriter;
7import java.io.IOException;
8import java.io.Reader;
9import java.io.StringReader;
10import java.io.Writer;
11import java.util.Random;
12
13/**
14 * A string buffer that flushes its content to a temporary file whenever the internal
15 * string buffer becomes larger than MAX. If the buffer never reaches that size, no file
16 * is ever created and everything happens in memory, so the overhead compared to
17 * StringBuffer/StringBuilder is minimal.
18 *
19 * Note: calling toString() will force the entire string to be loaded in memory, use
20 * toWriter() if you need to avoid this.
21 *
22 * This class is not multi thread safe.
23 *
24 * @author Cedric Beust <cedric@beust.com>
25 *
26 * @since Nov 9, 2012
27 */
28public class FileStringBuffer implements IBuffer {
29  private static int MAX = 100000;
30  private static final boolean VERBOSE = System.getProperty("fileStringBuffer") != null;
31
32  private File m_file;
33  private StringBuilder m_sb = new StringBuilder();
34  private final int m_maxCharacters;
35
36  public FileStringBuffer() {
37    this(MAX);
38  }
39
40  public FileStringBuffer(int maxCharacters) {
41    m_maxCharacters = maxCharacters;
42  }
43
44  @Override
45  public FileStringBuffer append(CharSequence s) {
46    if (s == null) {
47      throw new IllegalArgumentException("CharSequence (Argument 0 of FileStringBuffer#append) should not be null");
48    }
49//    m_sb.append(s);
50    if (m_sb.length() > m_maxCharacters) {
51      flushToFile();
52    }
53    if (s.length() < MAX) {
54      // Small string, add it to our internal buffer
55      m_sb.append(s);
56    } else {
57      // Big string, add it to the temporary file directly
58      flushToFile();
59      try {
60        copy(new StringReader(s.toString()), new FileWriter(m_file, true /* append */));
61      } catch (IOException e) {
62        e.printStackTrace();
63      }
64    }
65    return this;
66  }
67
68  @Override
69  public void toWriter(Writer fw) {
70    if (fw == null) {
71      throw new IllegalArgumentException("Writer (Argument 0 of FileStringBuffer#toWriter) should not be null");
72    }
73    try {
74      BufferedWriter bw = new BufferedWriter(fw);
75      if (m_file == null) {
76        bw.write(m_sb.toString());
77        bw.close();
78      } else {
79        flushToFile();
80        copy(new FileReader(m_file), bw);
81      }
82    } catch(IOException ex) {
83      ex.printStackTrace();
84    }
85  }
86
87  private static void copy(Reader input, Writer output)
88      throws IOException {
89    char[] buf = new char[MAX];
90    while (true) {
91      int length = input.read(buf);
92      if (length < 0) break;
93      output.write(buf, 0, length);
94    }
95
96    try {
97      input.close();
98    } catch (IOException ignore) {
99    }
100    try {
101      output.close();
102    } catch (IOException ignore) {
103    }
104  }
105
106  private void flushToFile() {
107    if (m_sb.length() == 0) return;
108
109    if (m_file == null) {
110      try {
111        m_file = File.createTempFile("testng", "fileStringBuffer");
112        m_file.deleteOnExit();
113        p("Created temp file " + m_file);
114      } catch (IOException e) {
115        e.printStackTrace();
116      }
117    }
118
119    p("Size " + m_sb.length() + ", flushing to " + m_file);
120    try (FileWriter fw = new FileWriter(m_file, true /* append */)) {
121      fw.append(m_sb);
122    } catch (IOException e) {
123      e.printStackTrace();
124    }
125    m_sb = new StringBuilder();
126  }
127
128  private static void p(String s) {
129    if (VERBOSE) {
130      System.out.println("[FileStringBuffer] " + s);
131    }
132  }
133
134  @Override
135  public String toString() {
136    String result = null;
137    if (m_file != null) {
138      flushToFile();
139      try {
140        result = Files.readFile(m_file);
141      } catch (IOException e) {
142        e.printStackTrace();
143      }
144    } else {
145      result = m_sb.toString();
146    }
147    return result;
148  }
149
150  private static void save(File expected, String s) throws IOException {
151    expected.delete();
152    try (FileWriter expectedWriter = new FileWriter(expected)) {
153      expectedWriter.append(s);
154    }
155  }
156
157  public static void main(String[] args) throws IOException {
158    String s = "abcdefghijklmnopqrstuvwxyz";
159    FileStringBuffer fsb = new FileStringBuffer(10);
160    StringBuilder control = new StringBuilder();
161    Random r = new Random();
162    for (int i = 0; i < 1000; i++) {
163      int start = Math.abs(r.nextInt() % 26);
164      int length = Math.abs(r.nextInt() % (26 - start));
165      String fragment = s.substring(start, start + length);
166      p("... Appending " + fragment);
167      fsb.append(fragment);
168      control.append(fragment);
169    }
170
171    File expected = new File("/tmp/expected");
172    expected.delete();
173    FileWriter expectedWriter = new FileWriter(expected);
174    expectedWriter.append(control);
175    expectedWriter.close();
176
177    File actual = new File("/tmp/actual");
178    actual.delete();
179    FileWriter actualWriter = new FileWriter(actual);
180    fsb.toWriter(actualWriter);
181    actualWriter.close();
182//    Assert.assertEquals(fsb.toString(), control.toString());
183  }
184
185}
186