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