/* * 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); } }