blob: b8995fab9878fbc6008c7e4ea26d149b4bdcbecf [file] [log] [blame]
// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
// * to you 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.apache.juneau.serializer;
import java.io.*;
import java.net.*;
import java.util.*;
import org.apache.juneau.*;
/**
* Simple wrapper around a standard {@link Writer} with additional methods.
*
* <p>
* Modeled after the Java ProcessBuilder class so that you can chain commands to reduce the need for string
* concatenation for performance reasons.
*
* <h5 class='section'>Example:</h5>
* <p class='bcode w800'>
* writer.append(<js>"foo"</js>).nl().i(5).append(<js>"bar"</js>);
* </p>
*/
public class SerializerWriter extends Writer {
/** The underlying writer. */
protected final Writer out;
/** Use-whitespace flag. */
protected final boolean useWhitespace;
/** Max indentation levels. */
protected final int maxIndent;
/** Trim strings flag. */
protected final boolean trimStrings;
/** The quote character being used by this writer. */
protected final char quoteChar;
/** The URI resolver of the request. */
protected final UriResolver uriResolver;
/**
* @param out The writer being wrapped.
* @param useWhitespace
* If <jk>true</jk>, calling {@link #cr(int)} will create an indentation and calling {@link #s()} will write a
* space character.
* @param maxIndent The maximum indentation level.
* @param trimStrings If <jk>true</jk>, strings should be trimmed before they're serialized.
* @param quoteChar The character to write when {@link #q()} is called.
* @param uriResolver The URI resolver for resolving URIs to absolute or root-relative form.
*/
public SerializerWriter(Writer out, boolean useWhitespace, int maxIndent, boolean trimStrings, char quoteChar,
UriResolver uriResolver) {
this.out = out;
this.useWhitespace = useWhitespace;
this.maxIndent = maxIndent;
this.trimStrings = trimStrings;
this.quoteChar = quoteChar;
this.uriResolver = uriResolver;
}
/**
* Performs a carriage return.
*
* <p>
* Adds a newline and the specified number of tabs (if the {@code useWhitespace} setting is enabled) to the output.
*
* @param depth The indentation.
* @throws IOException If a problem occurred trying to write to the writer.
* @return This object (for method chaining).
*/
public SerializerWriter cr(int depth) throws IOException {
if (useWhitespace && depth <= maxIndent)
return nl(depth).i(depth);
return this;
}
/**
* Performs a carriage return at the end of a line.
*
* <p>
* Adds a newline and the specified number of tabs (if the {@code useWhitespace} setting is enabled) to the output.
*
* @param depth The indentation.
* @throws IOException If a problem occurred trying to write to the writer.
* @return This object (for method chaining).
*/
public SerializerWriter cre(int depth) throws IOException {
if (useWhitespace && depth <= maxIndent-1)
return nl(depth).i(depth);
return this;
}
/**
* Writes an indent (if the {@code useWhitespace} setting is enabled), followed by text, followed by a newline
* (if the {@code useWhitespace} setting is enabled).
*
* @param indent The number of tabs to indent.
* @param text The text to write.
* @throws IOException If a problem occurred trying to write to the writer.
* @return This object.
*/
public SerializerWriter appendln(int indent, String text) throws IOException {
return append(indent, true, text);
}
/**
* Writes the specified text followed by a newline (if the {@code useWhitespace} setting is enabled).
*
* @param text The text to write.
* @throws IOException If a problem occurred trying to write to the writer.
* @return This object.
*/
public SerializerWriter appendln(String text) throws IOException {
return append(0, true, text);
}
/**
* Writes an indent (if the {@code useWhitespace} setting is enabled), followed by text.
*
* @param indent The number of tabs to indent.
* @param text The text to write.
* @throws IOException If a problem occurred trying to write to the writer.
* @return This object.
*/
public SerializerWriter append(int indent, String text) throws IOException {
return append(indent, false, text);
}
/**
* Writes an indent (if the {@code useWhitespace} setting is enabled), followed by text.
*
* @param indent The number of tabs to indent.
* @param c The character to write.
* @throws IOException If a problem occurred trying to write to the writer.
* @return This object.
*/
public SerializerWriter append(int indent, char c) throws IOException {
return i(indent).append(c);
}
/**
* Writes an indent (if the {@code useWhitespace} setting is enabled), followed by text, optionally followed by a
* newline (if the {@code useWhitespace} setting is enabled).
*
* @param indent The number of tabs to indent.
* @param newline If <jk>true</jk>, then a newline is written.
* @param text The text to write.
* @throws IOException If a problem occurred trying to write to the writer.
* @return This object (for method chaining).
*/
private SerializerWriter append(int indent, boolean newline, String text) throws IOException {
// If text contains newlines, we break it up into lines and indent them separately.
if (text.indexOf('\n') != -1 && useWhitespace && indent <= maxIndent) {
for (StringTokenizer st = new StringTokenizer(text, "\n"); st.hasMoreTokens();) {
i(indent);
out.write(st.nextToken());
out.write("\n");
}
} else {
i(indent);
out.write(text);
}
if (newline)
nl(indent);
return this;
}
/**
* Appends the specified object as a URI.
*
* <p>
* Object is converted to a <c>String</c> using <c>toString()</c>, so this will work on {@link URL} or
* {@link URI} objects, or any other type that returns a URI via it's <c>toString()</c> method.
*
* <p>
* The URI is resolved based on the {@link Serializer#SERIALIZER_uriRelativity} and
* {@link Serializer#SERIALIZER_uriResolution} settings and the {@link UriContext} that's part of the
* session.
*
* @param uri The URI to serialize.
* @return This object (for method chaining).
* @throws IOException If a problem occurred trying to write to the writer.
*/
public SerializerWriter appendUri(Object uri) throws IOException {
uriResolver.append(this, uri);
return this;
}
/**
* Appends the specified characters to this writer.
*
* @param characters The characters to append to this writer.
* @return This object (for method chaining).
* @throws IOException Thrown by underlying stream.
*/
public SerializerWriter append(char[] characters) throws IOException {
for (char c : characters)
append(c);
return this;
}
/**
* Adds a whitespace character to the output if the {@code useWhitespace} setting is enabled.
*
* @return This object (for method chaining).
* @throws IOException If a problem occurred trying to write to the writer.
*/
public SerializerWriter s() throws IOException {
if (useWhitespace)
out.write(' ');
return this;
}
/**
* Adds the quote character specified by the {@code quoteChar} setting to the output.
*
* @return This object (for method chaining).
* @throws IOException If a problem occurred trying to write to the writer.
*/
public SerializerWriter q() throws IOException {
out.write(quoteChar);
return this;
}
/**
* Writes an indent to the writer if the {@code useWhitespace} setting is enabled.
*
* @param indent The number of tabs to indent.
* @throws IOException If a problem occurred trying to write to the writer.
* @return This object (for method chaining).
*/
public SerializerWriter i(int indent) throws IOException {
if (useWhitespace && indent <= maxIndent)
for (int i = 0; i < indent; i++)
out.write('\t');
return this;
}
/**
* Writes an end-of-line indent to the writer if the {@code useWhitespace} setting is enabled.
*
* @param indent The number of tabs to indent.
* @throws IOException If a problem occurred trying to write to the writer.
* @return This object (for method chaining).
*/
public SerializerWriter ie(int indent) throws IOException {
if (useWhitespace && indent <= maxIndent-1)
for (int i = 0; i < indent; i++)
out.write('\t');
return this;
}
/**
* Writes a newline to the writer if the {@code useWhitespace} setting is enabled.
*
* @param indent The current indentation level.
* @throws IOException If a problem occurred trying to write to the writer.
* @return This object (for method chaining).
*/
public SerializerWriter nl(int indent) throws IOException {
if (useWhitespace && indent <= maxIndent)
out.write('\n');
return this;
}
/**
* Writes a space if the boolean expression is <jk>true</jk> and {@code useWhitespace} is false.
*
* <p>
* Intended for cases in XML where text should be separated by either a space or newline.
* This ensures the text is separated by a space if whitespace is disabled.
*
* @param b The boolean flag.
* @return This object (for method chaining).
* @throws IOException If a problem occurred trying to write to the writer.
*/
public SerializerWriter sIf(boolean b) throws IOException {
if (b && ! useWhitespace)
out.write(' ');
return this;
}
/**
* Writes a newline to the writer if the {@code useWhitespace} setting is enabled and the boolean flag is true.
*
* @param b The boolean flag.
* @param indent The current indentation level.
* @return This object (for method chaining).
* @throws IOException If a problem occurred trying to write to the writer.
*/
public SerializerWriter nlIf(boolean b, int indent) throws IOException {
if (b && useWhitespace && indent <= maxIndent)
out.write('\n');
return this;
}
/**
* Writes the specified text to the writer.
*
* @param text The text to write.
* @throws IOException If a problem occurred trying to write to the writer.
* @return This object (for method chaining).
*/
public SerializerWriter append(Object text) throws IOException {
out.append(text == null ? null : text.toString());
return this;
}
/**
* Writes the specified text to the writer.
*
* @param text The text to write.
* @throws IOException If a problem occurred trying to write to the writer.
* @return This object (for method chaining).
*/
public SerializerWriter append(String text) throws IOException {
if (text != null)
out.append(text);
return this;
}
/**
* Writes the specified text to the writer if b is true.
*
* @param b Boolean flag.
* @param text The text to write.
* @throws IOException If a problem occurred trying to write to the writer.
* @return This object (for method chaining).
*/
public SerializerWriter appendIf(boolean b, String text) throws IOException {
if (b)
out.write(text);
return this;
}
/**
* Writes the specified text to the writer if b is true.
*
* @param b Boolean flag.
* @param c The text to write.
* @throws IOException If a problem occurred trying to write to the writer.
* @return This object (for method chaining).
*/
public SerializerWriter appendIf(boolean b, char c) throws IOException {
if (b)
out.write(c);
return this;
}
//-----------------------------------------------------------------------------------------------------------------
// Overridden methods
//-----------------------------------------------------------------------------------------------------------------
@Override /* Writer */
public SerializerWriter append(char c) throws IOException {
out.write(c);
return this;
}
@Override /* Writer */
public void write(char[] cbuf, int off, int len) throws IOException {
out.write(cbuf, off, len);
}
@Override /* Writer */
public void flush() throws IOException {
out.flush();
}
@Override /* Writer */
public void close() throws IOException {
out.close();
}
}