/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
* add the following below this CDDL HEADER, with the fields enclosed
* by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2007-2009 Sun Microsystems, Inc.
*/
package org.opends.messages;
import java.util.Locale;
import java.util.List;
import java.util.LinkedList;
import java.io.Serializable;
/**
* A builder used specifically for messages. As messages are
* appended they are translated to their string representation
* for storage using the locale specified in the constructor.
*
* 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. 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.
*/
@org.opends.server.types.PublicAPI(
stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
mayInstantiate=true,
mayExtend=false,
mayInvoke=true)
public final class MessageBuilder implements Appendable, CharSequence,
Serializable
{
private static final long serialVersionUID = -3292823563904285315L;
/** Used internally to store appended messages. */
private final StringBuilder sb = new StringBuilder();
/** Used internally to store appended messages. */
private final List<Message> messages = new LinkedList<Message>();
/** Used to render the string representation of appended messages. */
private final Locale locale;
/**
* Constructs an instance that will build messages
* in the default locale.
*/
public MessageBuilder() {
this(Locale.getDefault());
}
/**
* Constructs an instance that will build messages
* in the default locale having an initial message.
*
* @param message initial message
*/
public MessageBuilder(Message message) {
this(Locale.getDefault());
append(message);
}
/**
* Constructs an instance that will build messages
* in the default locale having an initial message.
*
* @param message initial message
*/
public MessageBuilder(String message) {
this(Locale.getDefault());
append(message);
}
/**
* Constructs an instance from another <code>MessageBuilder</code>.
*
* @param mb from which to construct a new message builder
*/
public MessageBuilder(MessageBuilder mb) {
for (Message msg : mb.messages) {
this.messages.add(msg);
}
this.sb.append(sb);
this.locale = mb.locale;
}
/**
* Constructs an instance that will build messages
* in a specified locale.
*
* @param locale used for translating appended messages
*/
public MessageBuilder(Locale locale) {
this.locale = locale;
}
/**
* Append a message to this builder. The string
* representation of the locale specifed in the
* constructor will be stored in this builder.
*
* @param message to be appended
* @return reference to this builder
*/
public MessageBuilder append(Message message) {
if (message != null) {
sb.append(message.toString(locale));
messages.add(message);
}
return this;
}
/**
* Append an integer to this builder.
*
* @param number to append
* @return reference to this builder
*/
public MessageBuilder append(int number) {
append(String.valueOf(number));
return this;
}
/**
* Append an object to this builder.
*
* @param object to append
* @return reference to this builder
*/
public MessageBuilder append(Object object) {
if (object != null) {
append(String.valueOf(object));
}
return this;
}
/**
* Append a string to this builder.
*
* @param cs to append
* @return reference to this builder
*/
public MessageBuilder append(CharSequence cs) {
if (cs != null) {
sb.append(cs);
if (cs instanceof Message) {
messages.add((Message)cs);
} else {
messages.add(Message.raw(cs));
}
}
return this;
}
/**
* Appends a subsequence of the specified character sequence to this
* <tt>Appendable</tt>.
*
* <p> An invocation of this method of the form <tt>out.append(csq, start,
* end)</tt> when <tt>csq</tt> is not <tt>null</tt>, behaves in
* exactly the same way as the invocation
*
* <pre>
* out.append(csq.subSequence(start, end)) </pre>
*
* @param csq
* The character sequence from which a subsequence will be
* appended. If <tt>csq</tt> is <tt>null</tt>, then characters
* will be appended as if <tt>csq</tt> contained the four
* characters <tt>"null"</tt>.
*
* @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 <tt>Appendable</tt>
*
* @throws IndexOutOfBoundsException
* If <tt>start</tt> or <tt>end</tt> are negative, <tt>start</tt>
* is greater than <tt>end</tt>, or <tt>end</tt> is greater than
* <tt>csq.length()</tt>
*/
public MessageBuilder append(CharSequence csq, int start, int end)
throws IndexOutOfBoundsException
{
return append(csq.subSequence(start, end));
}
/**
* Appends the specified character to this <tt>Appendable</tt>.
*
* @param c
* The character to append
*
* @return A reference to this <tt>Appendable</tt>
*/
public MessageBuilder append(char c) {
return append(String.valueOf(c));
}
/**
* Returns a string containing the characters in this sequence in the same
* order as this sequence. The length of the string will be the length of
* this sequence.
*
* @return a string consisting of exactly this sequence of characters
*/
public String toString() {
return sb.toString();
}
/**
* Returns a string representation of the appended content
* in the specific locale. Only <code>Message</code>s
* appended to this builder are rendered in the requested
* locale. Raw strings appended to this buffer are not
* translated to different locale.
*
* @param locale requested
* @return String representation
*/
public String toString(Locale locale) {
StringBuilder sb = new StringBuilder();
for (Message m : messages) {
sb.append(m.toString(locale));
}
return sb.toString();
}
/**
* Returns a raw message representation of the appended content.
* <p>
* If the first object appended to this <code>MessageBuilder</code>
* was a <code>Message</code> then the returned message will
* inherit its category and severity. Otherwise the returned message
* will have category {@link org.opends.messages.Category#USER_DEFINED}
* and severity {@link org.opends.messages.Severity#INFORMATION}.
*
* @return Message raw message representing builder content
*/
public Message toMessage() {
if(messages.isEmpty())
{
return Message.EMPTY;
}
StringBuffer fmtString = new StringBuffer();
for (int i = 0; i < messages.size(); i++) {
fmtString.append("%s");
}
// Inherit the category and severity of the first message.
MessageDescriptor md = messages.get(0).getDescriptor();
return Message.raw(md.getCategory(), md.getSeverity(), fmtString,
messages.toArray());
}
/**
* Returns the length of the string representation of this builder
* using the default locale.
*
* @return the number of <code>char</code>s in this message
*/
public int length() {
return length(Locale.getDefault());
}
/**
* Returns the <code>char</code> value at the specified index of
* the string representation of this builder using the default locale.
*
* @param index the index of the <code>char</code> value to be returned
*
* @return the specified <code>char</code> value
*
* @throws IndexOutOfBoundsException
* if the <tt>index</tt> argument is negative or not less than
* <tt>length()</tt>
*/
public char charAt(int index) throws IndexOutOfBoundsException {
return charAt(Locale.getDefault(), index);
}
/**
* Returns a new <code>CharSequence</code> that is a subsequence of
* the string representation of this builder using the default locale.
* The subsequence starts with the <code>char</code>
* value at the specified index and ends with the <code>char</code>
* value at index <tt>end - 1</tt>. The length (in <code>char</code>s)
* of the returned sequence is <tt>end - start</tt>, so if
* <tt>start == end</tt> 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 <tt>start</tt> or <tt>end</tt> are negative,
* if <tt>end</tt> is greater than <tt>length()</tt>,
* or if <tt>start</tt> is greater than <tt>end</tt>
*/
public CharSequence subSequence(int start, int end)
throws IndexOutOfBoundsException
{
return subSequence(Locale.getDefault(), start, end);
}
/**
* Returns the length of the string representation of this builder
* using a specific locale.
*
* @param locale for which the rendering of this message will be
* used in determining the length
* @return the number of <code>char</code>s in this message
*/
public int length(Locale locale) {
return toString(locale).length();
}
/**
* Returns the <code>char</code> value at the specified index of
* the string representation of this builder using a specific locale.
*
* @param locale for which the rendering of this message will be
* used in determining the character
* @param index the index of the <code>char</code> value to be returned
*
* @return the specified <code>char</code> value
*
* @throws IndexOutOfBoundsException
* if the <tt>index</tt> argument is negative or not less than
* <tt>length()</tt>
*/
public char charAt(Locale locale, int index)
throws IndexOutOfBoundsException
{
return toString(locale).charAt(index);
}
/**
* Returns a new <code>CharSequence</code> that is a subsequence of
* the string representation of this builder using a specific locale.
* The subsequence starts with the <code>char</code>
* value at the specified index and ends with the <code>char</code>
* value at index <tt>end - 1</tt>. The length (in <code>char</code>s)
* of the returned sequence is <tt>end - start</tt>, so if
* <tt>start == end</tt> then an empty sequence is returned.
*
* @param locale for which the rendering of this message will be
* used in determining the character
* @param start the start index, inclusive
* @param end the end index, exclusive
*
* @return the specified subsequence
*
* @throws IndexOutOfBoundsException
* if <tt>start</tt> or <tt>end</tt> are negative,
* if <tt>end</tt> is greater than <tt>length()</tt>,
* or if <tt>start</tt> is greater than <tt>end</tt>
*/
public CharSequence subSequence(Locale locale, int start, int end)
throws IndexOutOfBoundsException
{
return toString(locale).subSequence(start, end);
}
}