xref: /aosp_15_r20/external/cldr/tools/cldr-code/src/main/java/org/unicode/cldr/util/TempPrintWriter.java (revision 912701f9769bb47905792267661f0baf2b85bed5)
1 package org.unicode.cldr.util;
2 
3 import com.ibm.icu.util.ICUUncheckedIOException;
4 import java.io.BufferedReader;
5 import java.io.File;
6 import java.io.FileReader;
7 import java.io.IOException;
8 import java.io.PrintWriter;
9 import java.io.Writer;
10 import java.nio.file.Files;
11 import java.nio.file.Path;
12 import java.nio.file.StandardCopyOption;
13 import java.util.Random;
14 import org.unicode.cldr.draft.FileUtilities;
15 import org.unicode.cldr.tool.ShowLocaleCoverage;
16 
17 /**
18  * Simple utility to create a temporary file, write into it, then close it. If the file differs from
19  * the old file (except for date), then it is deleted. Otherwise it replaces the target file. Moved
20  * from UnicodeTools.
21  *
22  * @author markdavis
23  */
24 public class TempPrintWriter extends Writer {
25     final PrintWriter tempPrintWriter;
26     final String tempName;
27     final String filename;
28     boolean noReplace = false;
29     boolean skipCopyright = false;
30 
skipCopyright(boolean newSkipCopyright)31     public TempPrintWriter skipCopyright(boolean newSkipCopyright) {
32         skipCopyright = newSkipCopyright;
33         return this;
34     }
35 
asPrintWriter()36     public PrintWriter asPrintWriter() {
37         return tempPrintWriter;
38     }
39 
openUTF8Writer(String filename)40     public static TempPrintWriter openUTF8Writer(String filename) {
41         return new TempPrintWriter(new File(filename));
42     }
43 
openUTF8Writer(String dir, String filename)44     public static TempPrintWriter openUTF8Writer(String dir, String filename) {
45         return new TempPrintWriter(new File(dir, filename));
46     }
47 
TempPrintWriter(String dir, String filename)48     public TempPrintWriter(String dir, String filename) {
49         this(new File(dir, filename));
50     }
51 
TempPrintWriter(File file)52     public TempPrintWriter(File file) {
53         super();
54         final String parentFile = file.getParent();
55         this.filename = file.toString();
56         Random rand = new Random();
57         try {
58             File tempFile;
59             do {
60                 tempFile = new File(parentFile, (0xFFFF & rand.nextInt()) + "-" + file.getName());
61             } while (tempFile.exists());
62             tempName = tempFile.toString();
63             tempPrintWriter = FileUtilities.openUTF8Writer(parentFile, tempFile.getName());
64         } catch (IOException e) {
65             throw new ICUUncheckedIOException(e);
66         }
67     }
68 
dontReplaceFile()69     public void dontReplaceFile() {
70         noReplace = true;
71     }
72 
73     @Override
close()74     public void close() {
75         tempPrintWriter.close();
76         try {
77             if (noReplace) {
78                 new File(tempName).delete();
79             } else {
80                 replaceDifferentOrDelete(filename, tempName, skipCopyright);
81             }
82         } catch (IOException e) {
83             throw new ICUUncheckedIOException(e);
84         }
85     }
86 
87     @Override
write(char[] cbuf, int off, int len)88     public void write(char[] cbuf, int off, int len) {
89         tempPrintWriter.write(cbuf, off, len);
90     }
91 
92     @Override
flush()93     public void flush() {
94         tempPrintWriter.flush();
95     }
96 
println(Object line)97     public void println(Object line) {
98         tempPrintWriter.println(line);
99     }
100 
print(Object line)101     public void print(Object line) {
102         tempPrintWriter.print(line);
103     }
104 
println()105     public void println() {
106         tempPrintWriter.println();
107     }
108 
109     /** Println with extra tabs on each line, as needed, to appear as table in github */
printlnWithTabs(int desiredCount, String textToPrint)110     public void printlnWithTabs(int desiredCount, String textToPrint) {
111         StringBuilder result = new StringBuilder();
112         for (String line : ShowLocaleCoverage.LF_SPLITTER.split(textToPrint)) {
113             long count = desiredCount - line.chars().filter(ch -> ch == '\t').count();
114             if (count < 0) {
115                 throw new IllegalArgumentException("Too many tabs in line.");
116             }
117             result.append(line);
118             if (count != 0) {
119                 for (int i = 0; i < count; ++i) {
120                     result.append('\t');
121                 }
122             }
123             result.append('\n');
124         }
125         print(result);
126     }
127 
128     /**
129      * If contents(newFile) ≠ contents(oldFile), rename newFile to old. Otherwise delete newfile.
130      * Return true if replaced. *
131      */
replaceDifferentOrDelete( String oldFile, String newFile, boolean skipCopyright)132     private static boolean replaceDifferentOrDelete(
133             String oldFile, String newFile, boolean skipCopyright) throws IOException {
134         final File oldFile2 = new File(oldFile);
135         if (oldFile2.exists()) {
136             final String lines[] = new String[2];
137             final boolean identical = filesAreIdentical(oldFile, newFile, skipCopyright, lines);
138             if (identical) {
139                 new File(newFile).delete();
140                 return false;
141             }
142             System.out.println("Found difference in : " + oldFile + ", " + newFile);
143             final int diff = compare(lines[0], lines[1]);
144             System.out.println(
145                     " File1: '"
146                             + lines[0].substring(0, diff)
147                             + "', '"
148                             + lines[0].substring(diff)
149                             + "'");
150             System.out.println(
151                     " File2: '"
152                             + lines[1].substring(0, diff)
153                             + "', '"
154                             + lines[1].substring(diff)
155                             + "'");
156         }
157         Files.move(Path.of(newFile), oldFile2.toPath(), StandardCopyOption.REPLACE_EXISTING);
158         return true;
159     }
160 
filesAreIdentical( String file1, String file2, boolean skipCopyright, String[] lines)161     private static boolean filesAreIdentical(
162             String file1, String file2, boolean skipCopyright, String[] lines) throws IOException {
163         if (file1 == null) {
164             lines[0] = null;
165             lines[1] = null;
166             return false;
167         }
168         final BufferedReader br1 = new BufferedReader(new FileReader(file1), 32 * 1024);
169         final BufferedReader br2 = new BufferedReader(new FileReader(file2), 32 * 1024);
170         String line1 = "";
171         String line2 = "";
172         try {
173             for (int lineCount = 0; ; ++lineCount) {
174                 line1 = getLineWithoutFluff(br1, lineCount == 0, skipCopyright);
175                 line2 = getLineWithoutFluff(br2, lineCount == 0, skipCopyright);
176                 if (line1 == null) {
177                     if (line2 == null) {
178                         return true;
179                     }
180                     break;
181                 }
182                 if (!line1.equals(line2)) {
183                     break;
184                 }
185             }
186             lines[0] = line1;
187             lines[1] = line2;
188             if (lines[0] == null) {
189                 lines[0] = "<end of file>";
190             }
191             if (lines[1] == null) {
192                 lines[1] = "<end of file>";
193             }
194             return false;
195         } finally {
196             br1.close();
197             br2.close();
198         }
199     }
200 
getLineWithoutFluff( BufferedReader br1, boolean first, boolean skipCopyright)201     private static String getLineWithoutFluff(
202             BufferedReader br1, boolean first, boolean skipCopyright) throws IOException {
203         while (true) {
204             String line1 = br1.readLine();
205             if (line1 == null) {
206                 return line1;
207             }
208             line1 = line1.trim();
209             if (line1.length() == 0) {
210                 continue;
211             }
212             if (line1.equals("#")) {
213                 continue;
214             }
215             if (line1.startsWith("# Generated")) {
216                 continue;
217             }
218             if (line1.startsWith("# Date")) {
219                 continue;
220             }
221             if (skipCopyright
222                     && (line1.startsWith("# Copyright") || line1.contains("Copyright ©"))) {
223                 continue;
224             }
225             if (line1.startsWith("<p><b>Date:</b>")) {
226                 continue;
227             }
228             if (line1.startsWith("<td valign=\"top\">20") && line1.endsWith("GMT</td>")) {
229                 continue;
230             }
231 
232             if (line1.equals("# ================================================")) {
233                 continue;
234             }
235             if (first && line1.startsWith("#")) {
236                 first = false;
237                 continue;
238             }
239             return line1;
240         }
241     }
242 
243     /** Returns -1 if strings are equal; otherwise the first position they are different at. */
compare(String a, String b)244     public static int compare(String a, String b) {
245         int len = a.length();
246         if (len > b.length()) {
247             len = b.length();
248         }
249         for (int i = 0; i < len; ++i) {
250             if (a.charAt(i) != b.charAt(i)) {
251                 return i;
252             }
253         }
254         if (a.length() != b.length()) {
255             return len;
256         }
257         return -1;
258     }
259 }
260