From 9fc58b05b69983fb586314c1ff97c4f8c2c41d00 Mon Sep 17 00:00:00 2001 From: "J. Neugebauer" Date: Thu, 8 Dec 2022 21:16:01 +0100 Subject: [PATCH] Dokumentation --- src/main/java/schule/ngb/zm/util/Counter.java | 105 ++++++++++++++- src/main/java/schule/ngb/zm/util/Noise.java | 4 +- src/main/java/schule/ngb/zm/util/Timer.java | 18 +-- .../java/schule/ngb/zm/util/Validator.java | 3 + .../schule/ngb/zm/util/io/FileLoader.java | 73 +++++++++-- .../zm/util/io/ResourceStreamProvider.java | 123 ++++++++++++++---- 6 files changed, 267 insertions(+), 59 deletions(-) diff --git a/src/main/java/schule/ngb/zm/util/Counter.java b/src/main/java/schule/ngb/zm/util/Counter.java index 4922b1c..acf41fa 100644 --- a/src/main/java/schule/ngb/zm/util/Counter.java +++ b/src/main/java/schule/ngb/zm/util/Counter.java @@ -5,7 +5,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** - * Eine Hilfsklasse um Dinge zu zählen. + * Eine Helferklasse, um Dinge zu zählen. *

* Im einfachsten Fall kann der Zähler als geteilte Zählvariable genutzt werden, * die mit {@link #inc()} und {@link #dec()} aus verschiedenen Objekten oder @@ -13,11 +13,11 @@ import java.util.concurrent.atomic.AtomicInteger; *

* Der Zähler kann aber auch Objekte zählen, indem die Instanzen an * {@link #count(Object)} übergeben werden. Am Ende kann mit {@link #getCount()} - * die Anzahl der Objkete abgerufen werden. + * die Anzahl der Objekte abgerufen werden. *

- * Handelt es sich bei den Objekten um Zahlen, dann merkt sich ein Zähler auch - * das Maximum, das Minimum, die Summe und den Durchschnitt der gezählten - * Werte. + * Handelt es sich bei den Objekten um Zahlen, dann merkt sich ein + * {@code Counter} auch das Maximum, das Minimum, die Summe und den Durchschnitt + * der gezählten Werte. *

* Ein Zähler kann auch komplette Arrays oder Listen von Zahlen zählen und die * obigen Statistiken auswerten. @@ -96,31 +96,42 @@ public final class Counter { } /** - * Gibt die aktuelle Anzahl zurück. * @return Die aktuelle Anzahl. */ public int getCount() { return count.get(); } + /** + * @return Das Maxium der bisher gezählten Werte. + */ public double getMax() { synchronized( count ) { return max; } } + /** + * @return Das Minimum der bisher gezählten Werte. + */ public double getMin() { synchronized( count ) { return min; } } + /** + * @return Die Summe der bisher gezählten Werte. + */ public double getSum() { synchronized( count ) { return sum; } } + /** + * @return Der Mittelwert der bisher gezählten Werte. + */ public double getAvg() { if( Double.isNaN(sum) ) { return Double.NaN; @@ -129,13 +140,27 @@ public final class Counter { } } - + /** + * Setzt den Zähler auf den angegebenen Wert. + *

+ * Die anderen Statistiken werden nicht verändert. + * + * @param count Der neue Wert des Zählers. + * @return Dieser Zähler selbst (method chaining). + */ @SuppressWarnings( "UnusedReturnValue" ) public Counter setCount( int count ) { this.count.set(count); return this; } + /** + * Setzt den Zähler auf Null. + *

+ * Die Statistiken werden auf {@link Double#NaN} gesetzt. + * + * @return Dieser Zähler selbst (method chaining). + */ @SuppressWarnings( "UnusedReturnValue" ) public Counter reset() { count.set(0); @@ -147,18 +172,40 @@ public final class Counter { return this; } + /** + * Erhöht den Zähler um Eins. + *

+ * Die anderen Statistiken werden nicht verändert. + * + * @return Dieser Zähler selbst (method chaining). + */ @SuppressWarnings( "UnusedReturnValue" ) public Counter inc() { this.count.incrementAndGet(); return this; } + /** + * Verringert den Zähler um Eins. + *

+ * Die anderen Statistiken werden nicht verändert. + * + * @return Dieser Zähler selbst (method chaining). + */ @SuppressWarnings( "UnusedReturnValue" ) public Counter dec() { this.count.decrementAndGet(); return this; } + /** + * Zählt den angegebenen Wert. + *

+ * Erhöht den Zähler um Eins und aktualisiert die Statistiken. + * + * @param value Der neue Wert. + * @return Dieser Zähler selbst (method chaining). + */ @SuppressWarnings( "UnusedReturnValue" ) public Counter count( double value ) { inc(); @@ -173,11 +220,27 @@ public final class Counter { return this; } + /** + * Zählt die angegebene Zahl. + *

+ * Erhöht den Zähler um Eins und aktualisiert die Statistiken. + * + * @param num Die neue Zahl. + * @return Dieser Zähler selbst (method chaining). + */ @SuppressWarnings( "UnusedReturnValue" ) public Counter count( Number num ) { return count(num.doubleValue()); } + /** + * Zählt das angegebenen Objekt. + *

+ * Erhöht den Zähler um Eins. + * + * @param obj Ein beliebiges Objekt. + * @return Dieser Zähler selbst (method chaining). + */ @SuppressWarnings( "UnusedReturnValue" ) public Counter count( Object obj ) { if( obj instanceof Number ) { @@ -188,6 +251,13 @@ public final class Counter { } } + /** + * Zöhlt alle Werte im angegebenen Array. + * + * @param values Das Array der neuen Werte. + * @return Dieser Zähler selbst (method chaining). + * @see #count(double) + */ @SuppressWarnings( "UnusedReturnValue" ) public synchronized Counter countAll( double[] values ) { for( double value : values ) { @@ -196,6 +266,13 @@ public final class Counter { return this; } + /** + * Zöhlt alle Werte im angegebenen Array. + * + * @param values Das Array der neuen Werte. + * @return Dieser Zähler selbst (method chaining). + * @see #count(double) + */ @SuppressWarnings( "UnusedReturnValue" ) public synchronized Counter countAll( int[] values ) { for( double value : values ) { @@ -204,6 +281,13 @@ public final class Counter { return this; } + /** + * Zöhlt alle Zahlen im angegebenen Array. + * + * @param values Das Array der neuen Zahlen. + * @return Dieser Zähler selbst (method chaining). + * @see #count(Number) + */ @SuppressWarnings( "UnusedReturnValue" ) public synchronized Counter countAll( Number[] values ) { for( Number value : values ) { @@ -212,6 +296,13 @@ public final class Counter { return this; } + /** + * Zöhlt alle Zahlen in der angegebenen Sammlung. + * + * @param values Die Sammlung der neuen Zahlen. + * @return Dieser Zähler selbst (method chaining). + * @see #count(Number) + */ @SuppressWarnings( "UnusedReturnValue" ) public synchronized Counter countAll( Collection values ) { for( Number value : values ) { diff --git a/src/main/java/schule/ngb/zm/util/Noise.java b/src/main/java/schule/ngb/zm/util/Noise.java index b019a89..e1cd34e 100644 --- a/src/main/java/schule/ngb/zm/util/Noise.java +++ b/src/main/java/schule/ngb/zm/util/Noise.java @@ -3,7 +3,7 @@ package schule.ngb.zm.util; import java.util.Random; /** - * Zufallsgenerator für Perlin-Noise. + * Generator für Perlin-Noise. *

* Die Implementierung basiert auf dem von Ken Perlin entwickelten Algorithmus * und wurde anhand der - * Die Klasse kann zum Beispiel genutzt werden, um die Laufzeit eines - * Quelltextes zu messen. Wie eine echte Stoppuhr läuft der {@code Timer} - * weiter, wenn nach einem {@link #stop()} wieder {@link #start()} aufgerufen - * wird. Soll die Zeitmessung wieder bei null beginnen, muss vorher - * {@link #reset()} genutzt werden. + * Mit einem {@code Timer} kann zum Beispiel die Laufzeit eines Algorithmus + * gemessen werden. Wie eine echte Stoppuhr läuft der {@code Timer} weiter, wenn + * nach einem {@link #stop()} wieder {@link #start()} aufgerufen wird. Soll die + * Zeitmessung wieder bei null beginnen, muss vorher {@link #reset()} genutzt + * werden. *

* Die gemessene Zeit kann in {@link #getMillis() Millisekunden} oder * {@link #getSeconds() Sekunden} abgerufen werden. Wird eine noch größere @@ -100,7 +100,7 @@ public final class Timer { } /** - * Stoppt den {@code Timer}, wenn er derzeit läuft. Die gemessene Zeit wird + * Stoppt den {@code Timer}, wenn er derzeit läuft. Die gemessene Dauer wird * zur Summe aller gemessenen Zeiten hinzuaddiert. * * @return Dieser {@code Timer} selbst (method chaining). @@ -116,8 +116,8 @@ public final class Timer { /** * Setzt den {@code Timer} auf den Startzustand und löscht alle bisher - * gemessenen Zeiten. Falls die Zeitmessung gerade läuft, wird sie nicht - * gestoppt, sondern läuft vom Zeitpunkt des Aufrufs weiter. + * gemessenen Zeiten. Falls die Zeitmessung gerade läuft, stoppt sie nicht, + * sondern startet vom Zeitpunkt des Aufrufs neu. * * @return Dieser {@code Timer} selbst (method chaining). */ diff --git a/src/main/java/schule/ngb/zm/util/Validator.java b/src/main/java/schule/ngb/zm/util/Validator.java index 1bd233e..b018275 100644 --- a/src/main/java/schule/ngb/zm/util/Validator.java +++ b/src/main/java/schule/ngb/zm/util/Validator.java @@ -3,6 +3,9 @@ package schule.ngb.zm.util; import java.util.Objects; import java.util.function.Supplier; +/** + * Statische Methoden, um Methodenparameter auf Gültigkeit zu prüfen. + */ @SuppressWarnings( "UnusedReturnValue" ) public class Validator { diff --git a/src/main/java/schule/ngb/zm/util/io/FileLoader.java b/src/main/java/schule/ngb/zm/util/io/FileLoader.java index 17fc59e..795d592 100644 --- a/src/main/java/schule/ngb/zm/util/io/FileLoader.java +++ b/src/main/java/schule/ngb/zm/util/io/FileLoader.java @@ -4,37 +4,63 @@ import schule.ngb.zm.util.Log; import java.io.BufferedReader; import java.io.IOException; -import java.net.URISyntaxException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.stream.Stream; +import java.util.function.Function; +/** + * Helferklasse, um Textdateien in verschiedenen Formaten einzulesen. + */ +@SuppressWarnings( "unused" ) public final class FileLoader { + /** + * Charset: ASCII + */ public static final Charset ASCII = StandardCharsets.US_ASCII; + /** + * Charset: UTF-8 + */ public static final Charset UTF8 = StandardCharsets.UTF_8; + /** + * Charset: UTF-16 + */ public static final Charset UTF16 = StandardCharsets.UTF_16; + /** + * Charset: ISO-8859-1 + */ public static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1; + /** + * Lädt die angegebene Textdatei zeilenweise in eine Liste. + *

+ * Als {@link Charset} wird {@link #UTF8} verwendet. + * + * @param source Die Quelle der Textdatei. + * @return Eine Liste mit den Zeilen der Textdatei. + * @see #loadLines(String, Charset) + */ public static List loadLines( String source ) { return loadLines(source, UTF8); } /** - * Lädt die angegebene Datei Zeilenweise in eine Liste. + * Lädt die angegebene Textdatei mit dem angegebenen Charset zeilenweise in + * eine Liste. + *

+ * Am Ende jeder Zeile wird das Symbol für einen Zeilenumbruch ({@code \n}) + * entfernt. * - * @param source - * @param charset - * @return + * @param source Die Quelle der Textdatei. + * @param charset Das zu verwendene {@code Charset}. + * @return Eine Liste mit den Zeilen der Textdatei. */ public static List loadLines( String source, Charset charset ) { try( BufferedReader reader = ResourceStreamProvider.getReader(source, charset) ) { @@ -52,10 +78,28 @@ public final class FileLoader { } } + /** + * Lädt den Inhalt der angegebenen Textdatei vollständig in einen String. + *

+ * Als {@link Charset} wird {@link #UTF8} verwendet. + * + * @param source Eine Quelle für die Textdatei (Absoluter Dateipfad, + * Dateipfad im Classpath oder Netzwerkressource) + * @return Der Inhalt der Textdatei. + */ public static String loadText( String source ) { return loadText(source, UTF8); } + /** + * Lädt den Inhalt der angegebenen Textdatei mit dem angegebenen + * {@code Charset} vollständig in einen String. + * + * @param source Eine Quelle für die Textdatei (Absoluter Dateipfad, + * Dateipfad im Classpath oder Netzwerkressource) + * @param charset Der {@code Charset} der Textdatei. + * @return Der Inhalt der Textdatei. + */ public static String loadText( String source, Charset charset ) { try( BufferedReader reader = ResourceStreamProvider.getReader(source, charset) ) { StringBuilder result = new StringBuilder(); @@ -73,15 +117,16 @@ public final class FileLoader { } /** - * Lädt die Daten aus einer CSV Datei in ein zweidimensionales + * Lädt die Daten aus einer CSV-Datei in ein zweidimensionales * String-Array. *

- * Der Aufruf entspricht dem Aufruf von + * Der Aufruf entspricht *


 	 * FileLoader.loadCsv(source, ',', skipFirst, UTF8);
 	 * 
* - * @param source Die Quelle der CSV-Daten. + * @param source Eine Quelle für die CSV-Datei (Absoluter Dateipfad, + * * Dateipfad im Classpath oder Netzwerkressource) * @param skipFirst Ob die erste Zeile übersprungen werden soll. * @return Ein Array mit den Daten als {@code String}s. * @see #loadCsv(String, char, boolean, Charset) @@ -91,12 +136,12 @@ public final class FileLoader { } /** - * Lädt die Daten aus einer CSV Datei in ein zweidimensionales + * Lädt die Daten aus einer CSV-Datei in ein zweidimensionales * String-Array. *

* Die Methode ist nicht in der Lage, komplexe CSV-Dateien zu verarbeiten. - * Insbesondere können Inhalte, die das Trennzeichen {@code separator} - * enthalten, nicht korrekt erkannt werden. Das Trennzeichen wird unabhängig + * Insbesondere werden Inhalte, die das Trennzeichen {@code separator} + * enthalten, nicht korrekt erkannt. Das Trennzeichen wird unabhängig * vom Kontext immer als Zelltrenner erkannt. (Im Normalfall kann das * Trennzeichen durch die Verwendung doppelter Anführungszeichen in der Art * {@code Inhalt,"Inhalt, der Komma enthält",Inhalt} maskiert werden.) diff --git a/src/main/java/schule/ngb/zm/util/io/ResourceStreamProvider.java b/src/main/java/schule/ngb/zm/util/io/ResourceStreamProvider.java index c0087da..9620ca9 100644 --- a/src/main/java/schule/ngb/zm/util/io/ResourceStreamProvider.java +++ b/src/main/java/schule/ngb/zm/util/io/ResourceStreamProvider.java @@ -13,39 +13,38 @@ import java.nio.file.Files; import java.nio.file.Path; /** - * Helferklasse, um {@link InputStream}s für Resourcen zu erhalten. + * Helferklasse, um {@link InputStream}s für Ressourcen zu erhalten. */ +@SuppressWarnings("unused") public class ResourceStreamProvider { - /** * Ermittelt zur angegebenen Quelle einen passenden {@link URL} (Unified - * Resource Locator). Eine passende Datei-Resource wird wie folgt + * Resource Locator). Eine passende Datei-Ressource wird wie folgt * ermittelt: *

    *
  1. Ist {@code source} eine existierende Datei * ({@code new File(source}.isFile() == true})?
  2. - *
  3. Ist {@code source} ein relativer Pfad im Projekt ({@code getResource(source) != null})?.
  4. + *
  5. Ist {@code source} ein relativer Pfad im Projekt ({@code getResource(source) != null})?
  6. *
  7. Ist {@code source} im Classpath enthalten ({@code getClassLoader().getResource(source) != null})?
  8. - *
  9. Ansonten erstellt ein {@link URL}-Objekt.
  10. + *
  11. Ansonsten erstelle ein {@link URL}-Objekt.
  12. *
*

* Ein {@code URL} für die erste gefundene Resource wird zurückgegeben. - * Auftretende Exceptions - * werden als {@link IOException} geworfen. *

- * Bei einer Exception werden die folgenden Quellen nicht mehr abgefragt. + * Auftretende Exceptions werden als {@link IOException} geworfen. + * Bei einer Exception werden die nachfolgenden Quellen nicht mehr abgefragt. * Eine {@link java.net.MalformedURLException} beim Konstruieren des {@code URL} * zu einer Datei verhindert daher, dass noch im Classpath gesucht wird. * - * @param source Eine Quelle für die Resource (Absoluter Dateipfad, - * Dateipfad im Classpath oder Netzwerkresource) - * @return Ein {@code InputStream} für die Resource + * @param source Eine Quelle für die Ressource (Absoluter Dateipfad, + * Dateipfad im Classpath oder Netzwerkressource) + * @return Ein {@code InputStream} für die Ressource * @throws NullPointerException Falls {@code source} {@code null} ist. * @throws IllegalArgumentException Falls {@code source} ein leerer String * ist. * @throws IOException Geworfen beim Erzeugen einer URL zu - * einer bestehenden Resource. + * einer bestehenden Ressource. */ public static URL getResourceURL( String source ) throws NullPointerException, IllegalArgumentException, IOException { Validator.requireNotNull(source, "Resource source may not be null"); @@ -66,50 +65,59 @@ public class ResourceStreamProvider { } /** - * Sucht eine zur angegebenen Quelle passende Resource und öffnet einen + * Sucht eine zur angegebenen Quelle passende Ressource und öffnet einen * passenden {@link InputStream}. Die konkrete Art des Streams hängt davon - * ab, welche Art an Resource gefunden wird: + * ab, welche Art an Ressource gefunden wird: *

    *
  1. Ist {@code source} eine existierende Datei - * {@code new File(source}.isFile() == true}, dann wird ein + * {@code new File(source).isFile() == true}, dann wird ein * {@link FileInputStream} erstellt.
  2. *
  3. Ist {@code source} ein relativer Pfad im Projekt, wird ein passender * Stream mit {@link Class#getResourceAsStream(String)} geöffnet.
  4. *
  5. Ist {@code source} im Classpath enthalten, wird ein passender Stream * mit {@link ClassLoader#getResourceAsStream(String)} geöffnet.
  6. - *
  7. Ist {@code source} eine gültige {@link URL}, dann wird ein + *
  8. Ist {@code source} eine gültige {@link URL}, dann wird ein * {@link URL#openStream() Netwerkstream} geöffnet.
  9. *
*

- * Die Möglichen Resourcen werden in der gelisteten Reihenfolge durchprobiert + * Die Möglichen Ressourcen werden in der gelisteten Reihenfolge durchprobiert * und der erste gefundene Stream zurückgegeben. Auftretende Exceptions * werden als {@link IOException} geworfen. Das bedeutet, falls für {@code source} - * keine gültige Resource gefunden werden kann, wird am Ende die + * keine gültige Ressource gefunden werden kann, wird am Ende die * von {@link URL#openStream()} erzeugte {@code IOException} geworfen. *

- * Bei einer Exception werden die folgenden Quellen nicht mehr abgefragt. + * Bei einer Exception werden die nachfolgenden Quellen nicht mehr abgefragt. * Eine {@link SecurityException} beim Öffnen des {@code FileInputStream} * (zum Beispiel, weil auf eine existierende Datei keine Leserechte bestehen) * verhindert daher, dass noch im Classpath gesucht wird. * - * @param source Eine Quelle für die Resource (Absoluter Dateipfad, - * Dateipfad im Classpath oder Netzwerkresource) - * @return Ein {@code InputStream} für die Resource + * @param source Eine Quelle für die Ressource (Absoluter Dateipfad, + * Dateipfad im Classpath oder Netzwerkresource). + * @return Ein {@code InputStream} für die Ressource. * @throws NullPointerException Falls {@code source} {@code null} ist. * @throws IllegalArgumentException Falls {@code source} ein leerer String * ist. * @throws IOException Geworfen beim Öffnen des Streams zu - * einer bestehenden Resource oder falls - * keine passende Resource gefunden wurde. + * einer bestehenden Ressource oder falls + * keine passende Ressource gefunden + * wurde. */ public static InputStream getInputStream( String source ) throws NullPointerException, IllegalArgumentException, IOException { return getResourceURL(source).openStream(); } /** - * @param source - * @return - * @throws IOException + * Sucht eine zur angegebenen Quelle passende Ressource und öffnet einen + * passenden {@link OutputStream}. + *

+ * Es wird davon ausgegangen, dass die Quelle auf eine Datei verweist, auf + * die Schreibrechte bestehen. Ist dies nicht der Fall, wird eine + * {@link IOException} geworfen. + * + * @param source Eine Quelle für die Ressource (Absoluter Dateipfad, + * Dateipfad im Classpath). + * @return {@code OutputStream} für die Ressource. + * @throws IOException Falls die Quelle kein gültiges Ziel beschreibt. */ public static OutputStream getOutputStream( String source ) throws IOException { try { @@ -119,22 +127,83 @@ public class ResourceStreamProvider { } } + /** + * Sucht eine zur angegebenen Quelle passende Ressource und öffnet einen + * {@link BufferedReader}. + *

+ * Die Suche erfolgt, wie bei {@link #getInputStream(String)} beschrieben. + *

+ * Als {@link Charset} wird {@link StandardCharsets#UTF_8 UTF-8} verwendet. + * + * @param source Eine Quelle für die Ressource (Absoluter Dateipfad, + * Dateipfad im Classpath oder Netzwerkresource). + * @return Ein {@code BufferedReader} für die Quelle. + * @throws IOException Falls die Quelle nicht exisitert oder nicht geöffnet + * werden kann. + * @see #getInputStream(String) + */ public static BufferedReader getReader( String source ) throws IOException { return getReader(source, StandardCharsets.UTF_8); } + /** + * Sucht eine zur angegebenen Quelle passende Ressource und öffnet einen + * {@link BufferedReader} mit dem angegebenen Charset. + *

+ * Die Suche erfolgt wie bei {@link #getInputStream(String)} beschrieben. + * + * @param source Eine Quelle für die Ressource (Absoluter Dateipfad, + * Dateipfad im Classpath oder Netzwerkresource). + * @param charset Der zu verwendende Charset. + * @return Ein {@code BufferedReader} für die Quelle. + * @throws IOException Falls die Quelle nicht exisitert oder nicht geöffnet + * werden kann. + * @see #getInputStream(String) + */ public static BufferedReader getReader( String source, Charset charset ) throws IOException { return new BufferedReader(new InputStreamReader(getInputStream(source), charset.newDecoder())); } + /** + * Sucht eine zur angegebenen Quelle passende Datei und öffnet einen + * {@link BufferedWriter}. + *

+ * Die Suche erfolgt wie bei {@link #getOutputStream(String)} beschrieben. + *

+ * Als {@link Charset} wird {@link StandardCharsets#UTF_8 UTF-8} verwendet. + * + * @param source Eine Quelle für die Ressource (Absoluter Dateipfad, + * Dateipfad im Classpath oder Netzwerkresource). + * @return Ein {@code BufferedWriter} für die Quelle. + * @throws IOException Falls die Quelle nicht exisitert oder nicht geöffnet + * werden kann. + * @see #getOutputStream(String) + */ public static BufferedWriter getWriter( String source ) throws IOException { return getWriter(source, StandardCharsets.UTF_8); } + /** + * Sucht eine zur angegebenen Quelle passende Datei und öffnet einen + * {@link BufferedWriter}. + *

+ * Die Suche erfolgt wie bei {@link #getOutputStream(String)} beschrieben. + *

+ * Als {@link Charset} wird {@link StandardCharsets#UTF_8 UTF-8} verwendet. + * + * @param source Eine Quelle für die Ressource (Absoluter Dateipfad, + * Dateipfad im Classpath oder Netzwerkresource). + * @return Ein {@code BufferedWriter} für die Quelle. + * @param charset Der zu verwendende Charset. + * @throws IOException Falls die Quelle nicht exisitert oder nicht geöffnet + * werden kann. + * @see #getOutputStream(String) + */ public static BufferedWriter getWriter( String source, Charset charset ) throws IOException { return new BufferedWriter(new OutputStreamWriter(getOutputStream(source), charset.newEncoder())); } + // Privater C'tor private ResourceStreamProvider() { }