com.redhat.ceylon.common.config.ConfigWriter Maven / Gradle / Ivy
package com.redhat.ceylon.common.config;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import com.redhat.ceylon.common.FileUtil;
/**
* Serializes {@link CeylonConfig} instances to streams and files.
*
* All serialization uses the UTF-8 character encoding.
*/
public class ConfigWriter {
/**
* Write the given configuration to the given file. Updating the existing file if
* it exists or otherwise creating a new file.
*/
public static void write(CeylonConfig config, File destination) throws IOException {
OutputStream out = null;
if (destination.isFile()) {
write(config, destination, destination);
} else {
try {
// First create any parent directories if necessary
File parentDir = destination.getAbsoluteFile().getParentFile();
if (!parentDir.exists()) {
FileUtil.mkdirs(parentDir);
}
// Now create the file itself
out = new FileOutputStream(destination);
write(config, out);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) { }
}
}
}
}
/**
* Reads config from the given source file, updating it using the given
* configuration and writing in to the destination file.
*/
public static void write(CeylonConfig config, File source, File destination) throws IOException {
boolean overwriteSource = destination.getCanonicalFile().equals(source.getCanonicalFile());
if (source.isFile()) {
InputStream in = null;
OutputStream out = null;
File tmpFile = null;
boolean ok = false;
try {
try {
in = new FileInputStream(source);
if (overwriteSource) {
// Send the output to a temporary file first
tmpFile = File.createTempFile(source.getName(), ".tmp", source.getAbsoluteFile().getParentFile());
out = new FileOutputStream(tmpFile);
} else {
// First create any parent directories if necessary
File parentDir = destination.getAbsoluteFile().getParentFile();
if (!parentDir.exists()) {
FileUtil.mkdirs(parentDir);
}
// Now create the file itself
out = new FileOutputStream(destination);
}
write(config, in, out);
ok = true;
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) { }
}
if (in != null) {
try {
in.close();
} catch (IOException e) { }
}
}
if (ok) {
File sourceBackup = new File(source.getAbsoluteFile().getParentFile(), source.getName() + "~");
Files.deleteIfExists(sourceBackup.toPath());
Files.move(source.toPath(), sourceBackup.toPath());
Files.move(tmpFile.toPath(), source.toPath());
}
} finally {
if (tmpFile != null) {
Files.deleteIfExists(tmpFile.toPath());
}
}
} else {
throw new FileNotFoundException("Couldn't open source configuration file");
}
}
/**
* Reads config from the given source file, updating it using the given
* configuration and writing in to the given output.
*/
public static void write(CeylonConfig config, File source, OutputStream out) throws IOException {
if (source.isFile()) {
InputStream in = null;
try {
in = new FileInputStream(source);
write(config, in, out);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) { }
}
}
} else {
throw new FileNotFoundException("Couldn't open source configuration file");
}
}
public static void write(CeylonConfig config, InputStream in, File destination) throws IOException {
OutputStream out = null;
try {
// First create any parent directories if necessary
File parentDir = destination.getAbsoluteFile().getParentFile();
if (!parentDir.exists()) {
FileUtil.mkdirs(parentDir);
}
// Now create the file itself
out = new FileOutputStream(destination);
write(config, in, out);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) { }
}
}
}
/**
* Reads config from the given input, updating it using the given
* configuration and writing in to the given output.
*/
public static void write(CeylonConfig orgconfig, InputStream in, OutputStream out) throws IOException {
final CeylonConfig config = orgconfig.copy();
final Writer writer = new BufferedWriter(new OutputStreamWriter(out, Charset.forName("UTF-8")));
ConfigReader reader = new ConfigReader(in, new ImprovedConfigReaderListenerAdapter(new ImprovedConfigReaderListener() {
private boolean skipToNewline = false;
@Override
public void setup() throws IOException {
// Ignoring setup
}
@Override
public void onSection(String section, String text) throws IOException {
if (config.isSectionDefined(section)) {
writer.write(text);
skipToNewline = false;
} else {
skipToNewline = true;
}
}
@Override
public void onSectionEnd(String section) throws IOException {
writeOptions(writer, config, section);
}
@Override
public void onOption(String name, String value, String text) throws IOException {
if (config.isOptionDefined(name)) {
String[] newValues = config.getOptionValues(name);
if (value.equals(newValues[0])) {
// The value hasn't changed, we'll write the option *exactly* as it was
writer.write(text);
} else {
// The value has changed, we will write a new option
CeylonConfig.Key k = new CeylonConfig.Key(name);
writeOptionValue(writer, k.getOptionName(), newValues[0]);
}
removeOptionValue(name);
skipToNewline = false;
} else {
skipToNewline = true;
}
}
@Override
public void onComment(String text) throws IOException {
if (skipToNewline) {
skipToNewline = !text.contains("\n");
} else {
writer.write(text);
}
}
@Override
public void onWhitespace(String text) throws IOException {
if (skipToNewline) {
skipToNewline = !text.contains("\n");
} else {
writer.write(text);
}
}
@Override
public void cleanup() throws IOException {
// Ignoring cleanup
}
private void removeOptionValue(String name) {
String[] values = config.getOptionValues(name);
if (values.length > 1) {
values = Arrays.copyOfRange(values, 1, values.length);
config.setOptionValues(name, values);
} else {
config.removeOption(name);
}
}
}));
reader.process();
writer.flush();
// Now write what's left of the configuration to the output
writeSections(writer, config, out);
writer.flush();
}
/**
* Write the given configuration to the given output stream.
*/
public static void write(CeylonConfig orgconfig, OutputStream out) throws IOException {
final CeylonConfig config = orgconfig.copy();
final Writer writer = new BufferedWriter(new OutputStreamWriter(out, Charset.forName("UTF-8")));
writeSections(writer, config, out);
writer.flush();
}
private static void writeSections(Writer writer, CeylonConfig config, OutputStream out) throws IOException {
String[] sections = config.getSectionNames(null);
Arrays.sort(sections);
for (String section : sections) {
if (config.getOptionNames(section).length > 0) {
writer.write(System.lineSeparator());
writer.write("[");
String[] names = section.split("\\.");
if (names.length > 1) {
for (int i = 0; i < names.length - 1; i++) {
if (i > 0) {
writer.write(".");
}
writer.write(names[i]);
}
writer.write(" \"");
writer.write(names[names.length - 1]);
writer.write("\"");
} else {
writer.write(section);
}
writer.write("]");
writer.write(System.lineSeparator());
writeOptions(writer, config, section);
}
}
}
protected static void writeOptions(Writer writer, CeylonConfig config, String section) throws IOException {
String[] names = config.getOptionNames(section);
if (names != null) {
for (int i=0; i < names.length; i++) {
String name = names[i];
writeOption(writer, config, section, name);
config.removeOption(section + "." + name);
writer.write(System.lineSeparator());
}
}
}
protected static void writeOption(Writer writer, CeylonConfig config, String section, String name) throws IOException {
String[] values = config.getOptionValues(section + "." + name);
if (values != null) {
for (int i=0; i < values.length; i++) {
String value = values[i];
writeOptionValue(writer, name, value);
if (i < (values.length - 1)) {
writer.write(System.lineSeparator());
}
}
}
}
protected static void writeOptionValue(Writer writer, String name, String value) throws IOException {
writer.write(name);
writer.write("=");
writer.write(quote(value));
}
public static String escape(String value) {
value = value.replace("\\", "\\\\");
value = value.replace("\"", "\\\"");
value = value.replace("\t", "\\t");
value = value.replace("\n", "\\n");
return value;
}
public static String quote(String value) {
value = escape(value);
boolean needsQuotes = value.contains(";") || value.contains("#") || value.endsWith(" ");
if (needsQuotes) {
return "\"" + value + "\"";
} else {
return value;
}
}
}
interface ImprovedConfigReaderListener extends ConfigReaderListener {
public void onSectionEnd(String section) throws IOException;
}
// This adapter class improves on the standard ConfigReaderListener interface
// by adding an onSectionEnd() event which will be triggered at the end of
// each configuration section. It tries to be smart about this by considering
// whitespace and comments on the last option line to be still part of the
// last section while considering all whitespace and comments before a section
// line to be part of the new section
class ImprovedConfigReaderListenerAdapter implements ConfigReaderListener {
private ImprovedConfigReaderListener listener;
private String currentSection;
private boolean skipToNewline;
private ArrayList buffer;
interface Text {
String getText();
}
class Comment implements Text {
private String text;
public Comment(String text) {
this.text = text;
}
@Override
public String getText() {
return text;
}
}
class Whitespace implements Text {
private String text;
public Whitespace(String text) {
this.text = text;
}
@Override
public String getText() {
return text;
}
}
public ImprovedConfigReaderListenerAdapter(ImprovedConfigReaderListener listener) {
this.listener = listener;
this.currentSection = null;
this.skipToNewline = false;
this.buffer = new ArrayList();
}
@Override
public void setup() throws IOException {
// Ignoring setup
}
@Override
public void onSection(String section, String text) throws IOException {
if (currentSection != null) {
listener.onSectionEnd(currentSection);
}
flushBuffer();
currentSection = section;
listener.onSection(section, text);
skipToNewline = true;
}
@Override
public void onOption(String name, String value, String text) throws IOException {
flushBuffer();
listener.onOption(name, value, text);
skipToNewline = true;
}
@Override
public void onComment(String text) throws IOException {
if (skipToNewline) {
listener.onComment(text);
skipToNewline = !text.contains("\n");
} else {
buffer.add(new Comment(text));
}
}
@Override
public void onWhitespace(String text) throws IOException {
if (skipToNewline) {
listener.onWhitespace(text);
skipToNewline = !text.contains("\n");
} else {
buffer.add(new Whitespace(text));
}
}
@Override
public void cleanup() throws IOException {
if (currentSection != null) {
listener.onSectionEnd(currentSection);
}
flushBuffer();
}
private void flushBuffer() throws IOException {
for (Text t : buffer) {
if (t instanceof Comment) {
listener.onComment(t.getText());
} else if (t instanceof Whitespace) {
listener.onWhitespace(t.getText());
}
}
buffer.clear();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy