All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.digitalmediaserver.nsis.io.FormattedWriter Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2018 Digital Media Server
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.digitalmediaserver.nsis.io;

import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Formatter;
import java.util.Locale;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.digitalmediaserver.nsis.Utils;


/**
 * A {@link Writer} extension that overrides all calls to the super class using
 * {@link Writer} "as an interface". This class will, unlike {@link Writer},
 * handle {@link Locale} and {@link Charset} conversion. It also allows a
 * customized line separator and a buffer with a configurable approximate size.
 * 

* In addition to the inherited {@link #write} methods this class implements * {@link #writeln(String)}, {@link #newLine()}, * {@link #write(String, Object...)} and {@link #writeln(String, Object...)}. * The latter two methods allows formatting as if * {@link String#format(String, Object...)} called but with the {@link Locale} * from the constructor applied. *

* The buffer size is "lazy" in that it only checks between writes and flushes * if the threshold has been exceeded. The flushing is only of the buffer to the * underlying {@link OutputStream}, if the {@link OutputStream} is also buffered * it isn't flushed until the {@link FormattedWriter} is closed. * * @author Nadahar */ public class FormattedWriter extends Writer implements AutoCloseable { /** The default automatic flush threshold/approximate buffer size */ public static final int DEFAULT_AUTO_FLUSH_THRESHOLD = 1024; private final Formatter formatter; private final StringBuilder sb; private final OutputStream outputStream; private final Charset charset; private final int autoFlushThreshold; private final String lineSeparator; /** * Creates a new instance using the specified parameters. * * @param outputStream the {@link OutputStream} to write to. * @param locale the {@link Locale} to use for formatting. If {@code null}, * {@link Locale#ROOT} is used. * @param charset the {@link Charset} to use when writing. If {@code null}, * {@code UTF-8} is used. * @param lineSeparator the line separator to use with {@link #newLine()}, * {@link #writeln(String)} and * {@link #writeln(String, Object...)}. if blank the system * default line separator is used. * @param autoFlushThreshold the buffer flush threshold. If less than 64, * {@value #DEFAULT_AUTO_FLUSH_THRESHOLD} is used. */ public FormattedWriter( @Nonnull OutputStream outputStream, @Nullable Locale locale, @Nullable Charset charset, @Nullable String lineSeparator, int autoFlushThreshold ) { if (locale == null) { locale = Locale.ROOT; } this.charset = charset == null ? StandardCharsets.UTF_8 : charset; this.lineSeparator = Utils.isBlank(lineSeparator) ? System.lineSeparator() : lineSeparator; this.autoFlushThreshold = autoFlushThreshold > 64 ? autoFlushThreshold : DEFAULT_AUTO_FLUSH_THRESHOLD; sb = new StringBuilder((int) (this.autoFlushThreshold * 1.2)); this.outputStream = outputStream; this.formatter = new Formatter(sb, locale); } /** * Creates a new instance that writes to the specified {@link Path} using * {@link Locale#ROOT} and {@code UTF-8}. * * @param path the {@link Path} to write to. If the file already exists, it * will be overwritten. * @param lineSeparator the line separator to use with {@link #newLine()}, * {@link #writeln(String)} and * {@link #writeln(String, Object...)}. if blank the system * default line separator is used. * @throws IOException If an error occurs during the operation. */ public FormattedWriter( @Nonnull Path path, @Nullable String lineSeparator ) throws IOException { this(path, null, null, lineSeparator, 0); } /** * Creates a new instance that writes to the specified {@link Path}. * * @param path the {@link Path} to write to. If the file already exists, it * will be overwritten. * @param locale the {@link Locale} to use for formatting. If {@code null}, * {@link Locale#ROOT} is used. * @param charset the {@link Charset} to use when writing. If {@code null}, * {@code UTF-8} is used. * @param lineSeparator the line separator to use with {@link #newLine()}, * {@link #writeln(String)} and * {@link #writeln(String, Object...)}. if blank the system * default line separator is used. * @param autoFlushThreshold the buffer flush threshold. If less than 64, * {@value #DEFAULT_AUTO_FLUSH_THRESHOLD} is used. * @throws IOException If an error occurs during the operation. */ public FormattedWriter( @Nonnull Path path, @Nullable Locale locale, @Nullable Charset charset, @Nullable String lineSeparator, int autoFlushThreshold ) throws IOException { if (locale == null) { locale = Locale.ROOT; } this.charset = charset == null ? StandardCharsets.UTF_8 : charset; this.lineSeparator = Utils.isBlank(lineSeparator) ? System.lineSeparator() : lineSeparator; this.autoFlushThreshold = autoFlushThreshold > 64 ? autoFlushThreshold : DEFAULT_AUTO_FLUSH_THRESHOLD; sb = new StringBuilder((int) (this.autoFlushThreshold * 1.2)); this.outputStream = Files.newOutputStream(path); this.formatter = new Formatter(sb, locale); } @Override public void write(int c) throws IOException { sb.append(c); flushBuffer(true); } @Override public void write(@Nullable String string, int offset, int length) throws IOException { if (string == null) { return; } sb.append(string.substring(offset, offset + length)); flushBuffer(true); } @Override public void write(@Nullable char[] chars) throws IOException { if (chars == null) { return; } sb.append(chars); flushBuffer(true); } @Override public void write(char[] chars, int offset, int length) throws IOException { if (chars == null) { return; } sb.append(chars, offset, length); flushBuffer(true); } @Override public void write(@Nullable String string) throws IOException { if (string == null) { return; } sb.append(string); flushBuffer(true); } /** * Writes a line. * * @param string the {@link String} to write. * @throws IOException If an error occurs during the operation. */ public void writeln(@Nullable String string) throws IOException { if (string != null) { sb.append(string); } sb.append(lineSeparator); flushBuffer(true); } /** * Writes a formatted string as if * {@link String#format(Locale, String, Object...)} had been called. * * @param format the format string * @param args Arguments referenced by the format specifiers in the * {@code format} string. If there are more arguments than format * specifiers, the extra arguments are ignored. The number of * arguments is variable and may be zero. The maximum number of * arguments is limited by the maximum dimension of a Java array. * The behavior on a {@code null} argument depends on the * conversion. * @throws IOException If an error occurs during the operation. */ public void write(@Nullable String format, Object... args) throws IOException { if (format == null) { return; } formatter.format(format, args); flushBuffer(true); } /** * Writes a formatted line as if * {@link String#format(Locale, String, Object...)} had been called followed * by {@link #newLine()}. * * @param format the format string * @param args Arguments referenced by the format specifiers in the * {@code format} string. If there are more arguments than format * specifiers, the extra arguments are ignored. The number of * arguments is variable and may be zero. The maximum number of * arguments is limited by the maximum dimension of a Java array. * The behavior on a {@code null} argument depends on the * conversion. * @throws IOException If an error occurs during the operation. */ public void writeln(@Nullable String format, Object... args) throws IOException { if (format != null) { formatter.format(format, args); } sb.append(lineSeparator); flushBuffer(true); } @Override public Writer append(char c) throws IOException { sb.append(c); flushBuffer(true); return this; } @Override public Writer append(@Nullable CharSequence charSequence) throws IOException { if (charSequence == null) { return this; } sb.append(charSequence); flushBuffer(true); return this; } @Override public Writer append(@Nullable CharSequence charSequence, int start, int end) throws IOException { if (charSequence == null) { return this; } sb.append(charSequence.subSequence(start, end)); flushBuffer(true); return this; } /** * Writes the line separator specified in the constructor. * * @throws IOException If an error occurs during the operation. */ public void newLine() throws IOException { sb.append(lineSeparator); flushBuffer(true); } /** * Flushes the buffer to the {@link OutputStream} as needed. * * @param ifAboveThreshold if {@code true} the buffer will only be flushed * if it's larger than the threshold, if {@code false} it will be * flushed regardless. * @throws IOException If an error occurs during the operation. */ protected void flushBuffer(boolean ifAboveThreshold) throws IOException { if (!ifAboveThreshold && sb.length() > 0 || sb.length() >= autoFlushThreshold) { byte[] bytes = sb.toString().getBytes(charset); sb.setLength(0); outputStream.write(bytes); } } @Override public void flush() throws IOException { flushBuffer(false); outputStream.flush(); } @Override public void close() throws IOException { flush(); outputStream.close(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy