package FIXME; import java.io.*; import java.math.BigDecimal; import java.math.MathContext; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.text.DecimalFormat; import java.time.Duration; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import java.util.logging.Level; @SuppressWarnings("unused") public class FabianUtil { private static final Logger LOG = new Logger(FabianUtil.class); private static final String STD_DIR_TEMP = "/FIXME/temp/"; private static final String TEST_OUTPUT_DIR = "testOutput/"; public static final String ENDING_TXT = "txt"; public static final String ENDING_JSON = "json"; public static final String ENDING_HTML = "html"; public static final String ENDING_PDF = "pdf"; public static final String ENDING_TIFF = "tiff"; private FabianUtil() {} public static class Logger extends java.util.logging.Logger { public Logger(Class clazz) { this(java.util.logging.Logger.getLogger(clazz.getName())); } public Logger(java.util.logging.Logger logger) { super(logger.getName(), logger.getResourceBundleName()); } public void severe(String msg, Object param) { log(Level.SEVERE, msg, param); } public void severe(String msg, Object... params) { log(Level.SEVERE, msg, params); } public void warning(String msg, Object param) { log(Level.WARNING, msg, param); } public void warning(String msg, Object... params) { log(Level.WARNING, msg, params); } public void info(String msg, Object param) { log(Level.INFO, msg, param); } public void info(String msg, Object... params) { log(Level.INFO, msg, params); } public void config(String msg, Object param) { log(Level.CONFIG, msg, param); } public void config(String msg, Object... params) { log(Level.CONFIG, msg, params); } public void fine(String msg, Object param) { log(Level.FINE, msg, param); } public void fine(String msg, Object... params) { log(Level.FINE, msg, params); } public void finer(String msg, Object param) { log(Level.FINER, msg, param); } public void finer(String msg, Object... params) { log(Level.FINER, msg, params); } public void finest(String msg, Object param) { log(Level.FINEST, msg, param); } public void finest(String msg, Object... params) { log(Level.FINEST, msg, params); } } public static class ProgressHandler { private static final Logger LOG = new Logger(ProgressHandler.class); private static final DecimalFormat TIME_DECIMAL_FORMAT = new DecimalFormat("00"); protected static final int DEFAULT_LOG_DISTANCE = 50; protected static final int DEFAULT_TIME_LOG_DISTANCE = 15; private final ZonedDateTime startTime; private final BigDecimal logDistance; private final int timeLogDistance; private BigDecimal counter = BigDecimal.ZERO; private ZonedDateTime lastLoggedAt; protected final PrintStream printer; public ProgressHandler() { this(DEFAULT_LOG_DISTANCE, DEFAULT_TIME_LOG_DISTANCE, null); } public ProgressHandler(int logDistance) { this(logDistance, DEFAULT_TIME_LOG_DISTANCE, null); } public ProgressHandler(int logDistance, int timeLogDistance) { this(logDistance, timeLogDistance, null); } public ProgressHandler(int logDistance, int timeLogDistance, PrintStream printer) { startTime = ZonedDateTime.now(); this.logDistance = validateBD(logDistance, "Log-Distanz"); this.timeLogDistance = validateInt(timeLogDistance, "Zeitbasierte Log-Distanz"); this.printer = printer; } protected BigDecimal validateBD(int bd, String name) { return BigDecimal.valueOf(validateInt(bd, name)); } protected int validateInt(int n, String name) { if (n < 1) { throw new RuntimeException(name + " darf nicht 0 oder negativ sein."); } return n; } protected Logger logger() { return LOG; } protected final synchronized BigDecimal counter() { return counter; } protected final synchronized int timeLogDistance() { return timeLogDistance; } public synchronized void tickAndLog() { tick(); logIf(); } protected synchronized void tick() { counter = counter.add(BigDecimal.ONE); } public synchronized void logAndTick() { logIf(); tick(); } public synchronized void logIf() { if (shouldLog()) { log(); } } protected synchronized boolean shouldLog() { return (printer != null || logger().isLoggable(Level.INFO)) && (counter.remainder(logDistance).signum() == 0 || isBored()); } private synchronized boolean isBored() { return getTimeSinceLastLogged() >= timeLogDistance; } protected synchronized long getTimeSinceLastLogged() { ZonedDateTime time = lastLoggedAt == null ? startTime : lastLoggedAt; return time.until(ZonedDateTime.now(), ChronoUnit.SECONDS); } public synchronized void log() { Duration timePassed = getTimePassed(); if (printer != null) { printer.println(counter + " Stück erledigt. " + formatDuration(timePassed) + " vergangen."); } else { logger().info("{} Stück erledigt. {} vergangen.", counter, formatDuration(timePassed)); } updateLastLoggedAt(); } protected synchronized void updateLastLoggedAt() { lastLoggedAt = ZonedDateTime.now(); } protected synchronized Duration getTimePassed() { return Duration.ofSeconds(startTime.until(ZonedDateTime.now(), ChronoUnit.SECONDS)); } protected String formatDuration(Duration dur) { return formatNumber(dur.toHours()) + ":" + formatNumber(dur.toMinutesPart()) + ":" + formatNumber(dur.toSecondsPart()); } protected String formatNumber(Number n) { return TIME_DECIMAL_FORMAT.format(n == null ? 0 : n); } public synchronized void logFinal() { Duration timePassed = getTimePassed(); if (printer != null) { printer.println(counter + " Stück (100 %) erledigt. " + formatDuration(timePassed) + " vergangen."); } else { logger().info("{} Stück (100 %) erledigt. {} vergangen.", counter, formatDuration(timePassed)); } } } public static class ProgressHandlerWithTotal extends ProgressHandler { private static final Logger LOG = new Logger(ProgressHandlerWithTotal.class); private static final DecimalFormat PERCENTAGE_FORMAT = new DecimalFormat("00.00"); private static final BigDecimal ONE_HUNDRED_PERCENT = BigDecimal.valueOf(100); private final BigDecimal total; public ProgressHandlerWithTotal(int total) { this(DEFAULT_LOG_DISTANCE, DEFAULT_TIME_LOG_DISTANCE, total, null); } public ProgressHandlerWithTotal(int logDistance, int total) { this(logDistance, DEFAULT_TIME_LOG_DISTANCE, total, null); } public ProgressHandlerWithTotal(int logDistance, int timeLogDistance, int total) { this(logDistance, timeLogDistance, total, null); } public ProgressHandlerWithTotal(int logDistance, int timeLogDistance, int total, PrintStream printer) { super(logDistance, timeLogDistance, printer); this.total = validateBD(total, "Gesamt-Anzahl"); } @Override protected Logger logger() { return LOG; } @Override protected synchronized boolean shouldLog() { return super.shouldLog() && counter().compareTo(total) <= 0; } @Override public synchronized void log() { BigDecimal counter = counter(); BigDecimal percentage = ONE_HUNDRED_PERCENT.divide(total, MathContext.DECIMAL64).multiply(counter); Duration timePassed = getTimePassed(); String remainingTimePart = ""; if (counter.signum() != 0) { BigDecimal secondsPassed = BigDecimal.valueOf(timePassed.getSeconds()); BigDecimal totalSeconds = secondsPassed.divide(counter, MathContext.DECIMAL64).multiply(total); BigDecimal secondsToGo = totalSeconds.subtract(secondsPassed); Duration timeToGo = Duration.of(secondsToGo.longValue(), ChronoUnit.SECONDS); remainingTimePart = ", " + formatDuration(timeToGo) + " verbleibend"; } String counterPrint = total.compareTo(BigDecimal.TEN) >= 0 ? formatNumber(counter) : counter.toString(); if (printer != null) { printer.println(counterPrint + " Stück von " + total + " (" + PERCENTAGE_FORMAT.format(percentage) + " %) erledigt. " + formatDuration(timePassed) + " vergangen" + remainingTimePart + "."); } else { logger().info("{} Stück von {} ({} %) erledigt. {} vergangen{}.", counterPrint, total, PERCENTAGE_FORMAT.format(percentage), formatDuration(timePassed), remainingTimePart); } updateLastLoggedAt(); } public void logContinuouslyTimeBased() { new Thread(this::logTimeBased).start(); } private synchronized void logTimeBased() { while (counter().compareTo(total) != 0) { logIf(); long timeSinceLastLogged = getTimeSinceLastLogged(); try { TimeUnit.SECONDS.sleep(timeSinceLastLogged - timeLogDistance()); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); return; } } } } public static void writeTestOutput(String filename, String ending, String content) { writeToFile(STD_DIR_TEMP + TEST_OUTPUT_DIR, filename, ending, content); } public static void writeTestOutput(String filename, String ending, byte[] content) { writeToFile(STD_DIR_TEMP + TEST_OUTPUT_DIR, filename, ending, content); } public static void writeTestOutput(String filename, String ending, InputStream content) { writeToFile(STD_DIR_TEMP + TEST_OUTPUT_DIR, filename, ending, content); } public static void writeTestOutput(String filename, String ending, Reader content) { writeToFile(STD_DIR_TEMP + TEST_OUTPUT_DIR, filename, ending, content); } public static void writeDump(String ending, String content) { writeToFile(STD_DIR_TEMP, "dump", ending, content); } public static void writeDump(String ending, byte[] content) { writeToFile(STD_DIR_TEMP, "dump", ending, content); } public static void writeDump(String ending, InputStream content) { writeToFile(STD_DIR_TEMP, "dump", ending, content); } public static void writeDump(String ending, Reader content) { writeToFile(STD_DIR_TEMP, "dump", ending, content); } public static void writeDump(String filename, String ending, String content) { writeToFile(STD_DIR_TEMP, filename, ending, content); } public static void writeDump(String filename, String ending, byte[] content) { writeToFile(STD_DIR_TEMP, filename, ending, content); } public static void writeDump(String filename, String ending, InputStream content) { writeToFile(STD_DIR_TEMP, filename, ending, content); } public static void writeDump(String filename, String ending, Reader content) { writeToFile(STD_DIR_TEMP, filename, ending, content); } public static void writeToFile(String path, String filename, String ending, String content) { writeToFile(path, filename, ending, new StringReader(content)); } public static void writeToFile(String path, String filename, String ending, byte[] content) { writeToFile(path, filename, ending, new InputStreamReader(new ByteArrayInputStream(content))); } public static void writeToFile(String path, String filename, String ending, InputStream content) { writeToFile(path, filename, ending, new InputStreamReader(content)); } public static void writeToFile(String path, String filename, String ending, Reader content) { Path toFile = Paths.get(path + filename + "." + ending); LOG.fine("Schreibe in Datei {}", toFile); try (content) { BufferedWriter out = Files.newBufferedWriter(toFile); content.transferTo(out); /* varianten: mit streams: Files.copy(content, Paths.get(path + filename + "." + ending), StandardCopyOption.REPLACE_EXISTING); oder selber pipen, wie beim Reader/ writer: OutputStream out = Files.newOutputStream(Paths.get(path + filename + "." + ending)); content.transferTo(out); oder mit byte-array: Files.write(Paths.get(path + filename + "." + ending), content); */ } catch (IOException e) { throw new RuntimeException(e); } } }