LocalizableMessageBuilder.java
/*
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions Copyrighted [year] [name of copyright owner]".
*
* Copyright 2007-2009 Sun Microsystems, Inc.
* Portions copyright 2011 ForgeRock AS
*/
package org.forgerock.i18n;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
/**
* A mutable sequence of localizable messages and their parameters. As messages
* are appended they are translated to their string representation for storage
* using the locale specified in the constructor.
* <p>
* Note that before you use this class you should consider whether it is
* appropriate. In general composing messages by appending message to each other
* may not produce a message that is formatted appropriately for all locales.
* <p>
* It is usually better to create messages by composition. In other words you
* should create a base message that contains one or more string argument
* specifiers (%s) and define other message objects to use as replacement
* variables. In this way language translators have a change to reformat the
* message for a particular locale if necessary.
*
* @see LocalizableMessage
*/
public final class LocalizableMessageBuilder implements Appendable,
CharSequence, Serializable {
/**
* Generated serialization ID.
*/
private static final long serialVersionUID = -3292823563904285315L;
// Used internally to store appended messages.
private final List<LocalizableMessage> messages = new LinkedList<LocalizableMessage>();
/**
* Creates a new message builder whose content is initially empty.
*/
public LocalizableMessageBuilder() {
// Nothing to do.
}
/**
* Creates a new message builder whose content is initially equal to the
* provided message.
*
* @param message
* The initial content of the message builder.
* @throws NullPointerException
* If {@code message} was {@code null}.
*/
public LocalizableMessageBuilder(final LocalizableMessage message) {
append(message);
}
/**
* Creates a new message builder whose content is initially equal to the
* provided message builder.
*
* @param builder
* The initial content of the message builder.
* @throws NullPointerException
* If {@code builder} was {@code null}.
*/
public LocalizableMessageBuilder(final LocalizableMessageBuilder builder) {
for (final LocalizableMessage message : builder.messages) {
this.messages.add(message);
}
}
/**
* Creates a new message builder whose content is initially equal to the
* {@code String} representation of the provided {@code Object}.
*
* @param object
* The initial content of the message builder, may be
* {@code null}.
*/
public LocalizableMessageBuilder(final Object object) {
append(object);
}
/**
* Appends the provided character to this message builder.
*
* @param c
* The character to be appended.
* @return A reference to this message builder.
*/
public LocalizableMessageBuilder append(final char c) {
return append(LocalizableMessage.valueOf(c));
}
/**
* Appends the provided character sequence to this message builder.
*
* @param cs
* The character sequence to be appended.
* @return A reference to this message builder.
* @throws NullPointerException
* If {@code cs} was {@code null}.
*/
public LocalizableMessageBuilder append(final CharSequence cs) {
if (cs == null) {
throw new NullPointerException("cs was null");
}
return append((Object) cs);
}
/**
* Appends a subsequence of the provided character sequence to this message
* builder.
* <p>
* An invocation of this method of the form {@code append(cs, start, end)},
* behaves in exactly the same way as the invocation
*
* <pre>
* append(cs.subSequence(start, end))
* </pre>
*
* @param cs
* The character sequence to be appended.
* @param start
* The index of the first character in the subsequence.
* @param end
* The index of the character following the last character in the
* subsequence.
* @return A reference to this message builder.
* @throws IndexOutOfBoundsException
* If {@code start} or {@code end} are negative, {@code start}
* is greater than {@code end}, or {@code end} is greater than
* {@code csq.length()}.
* @throws NullPointerException
* If {@code cs} was {@code null}.
*/
public LocalizableMessageBuilder append(final CharSequence cs,
final int start, final int end) {
return append(cs.subSequence(start, end));
}
/**
* Appends the provided integer to this message builder.
*
* @param value
* The integer to be appended.
* @return A reference to this message builder.
*/
public LocalizableMessageBuilder append(final int value) {
return append(LocalizableMessage.valueOf(value));
}
/**
* Appends the provided message to this message builder.
*
* @param message
* The message to be appended.
* @return A reference to this message builder.
* @throws NullPointerException
* If {@code message} was {@code null}.
*/
public LocalizableMessageBuilder append(final LocalizableMessage message) {
if (message == null) {
throw new NullPointerException("message was null");
}
messages.add(message);
return this;
}
/**
* Appends the {@code String} representation of the provided {@code Object}
* to this message builder.
*
* @param object
* The object to be appended, may be {@code null}.
* @return A reference to this message builder.
*/
public LocalizableMessageBuilder append(final Object object) {
return append(LocalizableMessage.valueOf(object));
}
/**
* Returns the {@code char} value at the specified index of the
* {@code String} representation of this message builder in the default
* locale.
*
* @param index
* The index of the {@code char} value to be returned.
* @return The specified {@code char} value.
* @throws IndexOutOfBoundsException
* If the {@code index} argument is negative or not less than
* {@code length()}.
*/
public char charAt(final int index) {
return charAt(Locale.getDefault(), index);
}
/**
* Returns the {@code char} value at the specified index of the
* {@code String} representation of this message builder in the specified
* locale.
*
* @param locale
* The locale.
* @param index
* The index of the {@code char} value to be returned.
* @return The specified {@code char} value.
* @throws IndexOutOfBoundsException
* If the {@code index} argument is negative or not less than
* {@code length()}.
* @throws NullPointerException
* If {@code locale} was {@code null}.
*/
public char charAt(final Locale locale, final int index) {
return toString(locale).charAt(index);
}
/**
* Returns the length of the {@code String} representation of this message
* builder in the default locale.
*
* @return The length of the {@code String} representation of this message
* builder in the default locale.
*/
public int length() {
return length(Locale.getDefault());
}
/**
* Returns the length of the {@code String} representation of this message
* builder in the specified locale.
*
* @param locale
* The locale.
* @return The length of the {@code String} representation of this message
* builder in the specified locale.
* @throws NullPointerException
* If {@code locale} was {@code null}.
*/
public int length(final Locale locale) {
return toString(locale).length();
}
/**
* Returns a new {@code CharSequence} which is a subsequence of the
* {@code String} representation of this message builder in the default
* locale. The subsequence starts with the {@code char} value at the
* specified index and ends with the {@code char} value at index
* {@code end - 1} . The length (in {@code char}s) of the returned sequence
* is {@code end - start}, so if {@code start == end} then an empty sequence
* is returned.
*
* @param start
* The start index, inclusive.
* @param end
* The end index, exclusive.
* @return The specified subsequence.
* @throws IndexOutOfBoundsException
* If {@code start} or {@code end} are negative, if {@code end}
* is greater than {@code length()}, or if {@code start} is
* greater than {@code end}.
*/
public CharSequence subSequence(final int start, final int end) {
return subSequence(Locale.getDefault(), start, end);
}
/**
* Returns a new {@code CharSequence} which is a subsequence of the
* {@code String} representation of this message builder in the specified
* locale. The subsequence starts with the {@code char} value at the
* specified index and ends with the {@code char} value at index
* {@code end - 1} . The length (in {@code char}s) of the returned sequence
* is {@code end - start}, so if {@code start == end} then an empty sequence
* is returned.
*
* @param locale
* The locale.
* @param start
* The start index, inclusive.
* @param end
* The end index, exclusive.
* @return The specified subsequence.
* @throws IndexOutOfBoundsException
* If {@code start} or {@code end} are negative, if {@code end}
* is greater than {@code length()}, or if {@code start} is
* greater than {@code end}.
* @throws NullPointerException
* If {@code locale} was {@code null}.
*/
public CharSequence subSequence(final Locale locale, final int start,
final int end) {
return toString(locale).subSequence(start, end);
}
/**
* Returns the {@link LocalizableMessage} representation of this message
* builder. Subsequent changes to this message builder will not modify the
* returned {@code LocalizableMessage}.
*
* @return The {@code LocalizableMessage} representation of this message
* builder.
*/
public LocalizableMessage toMessage() {
if (messages.isEmpty()) {
return LocalizableMessage.EMPTY;
}
final int sz = messages.size();
final StringBuffer fmtString = new StringBuffer(sz * 2);
for (int i = 0; i < sz; i++) {
fmtString.append("%s");
}
return LocalizableMessage.raw(fmtString, messages.toArray());
}
/**
* Returns the {@code String} representation of this message builder in the
* default locale.
*
* @return The {@code String} representation of this message builder.
*/
@Override
public String toString() {
return toString(Locale.getDefault());
}
/**
* Returns the {@code String} representation of this message builder in the
* specified locale.
*
* @param locale
* The locale.
* @return The {@code String} representation of this message builder.
* @throws NullPointerException
* If {@code locale} was {@code null}.
*/
public String toString(final Locale locale) {
final StringBuilder builder = new StringBuilder();
for (final LocalizableMessage message : messages) {
builder.append(message.toString(locale));
}
return builder.toString();
}
}