/* * 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 * * * Portions Copyright 2007 Sun Microsystems, Inc. */ package org.opends.messages; import java.util.Locale; import java.util.Formatter; import java.util.Formattable; import java.util.IllegalFormatException; /** * Renders sensitive textural strings. In most cases message are intended * to render textural strings in a locale-sensitive manner although this * class defines convenience methods for creating uninternationalized * Message objects that render the same text regardless of * the requested locale. * * This class implements CharSequence so that messages can * be supplied as arguments to other messages. This way messages can * be composed of fragments of other messages if necessary. * * @see org.opends.messages.MessageDescriptor */ public class Message implements CharSequence, Formattable, Comparable { /** Represents an empty message string. */ public static final Message EMPTY = Message.raw(""); /** * Creates an uninternationalized message that will render itself * the same way regardless of the locale requested in * toString(Locale). The message will have a * category of Categore.USER_DEFINED and a severity * of Severity.INFORMATION * * Note that the types for args must be consistent with any * argument specifiers appearing in formatString according * to the rules of java.util.Formatter. A mismatch in type information * will cause this message to render without argument substitution. * * Before using this method you should be sure that the message you * are creating is locale sensitive. If so you should instead create * a formal message. * * @param formatString of the message or the message itself if not * arguments are necessary * @param args any arguments for the format string * @return a message object that will render the same in all locales; * null if formatString is null */ static public Message raw(CharSequence formatString, Object... args) { Message message = null; if (formatString != null) { message = new MessageDescriptor.Raw(formatString).get(args); } return message; } /** * Creates an uninternationalized message that will render itself * the same way regardless of the locale requested in * toString(Locale). * * Note that the types for args must be consistent with any * argument specifiers appearing in formatString according * to the rules of java.util.Formatter. A mismatch in type information * will cause this message to render without argument substitution. * * Before using this method you should be sure that the message you * are creating is locale sensitive. If so you should instead create * a formal message. * * @param category of this message * @param severity of this message * @param formatString of the message or the message itself if not * arguments are necessary * @param args any arguments for the format string * @return a message object that will render the same in all locales; * null if formatString is null */ static public Message raw(Category category, Severity severity, CharSequence formatString, Object... args) { Message message = null; if (formatString != null) { MessageDescriptor.Raw md = new MessageDescriptor.Raw(formatString, category, severity); message = md.get(args); } return message; } /** * Creates an uninternationalized message from the string representation * of an object. * * Note that the types for args must be consistent with any * argument specifiers appearing in formatString according * to the rules of java.util.Formatter. A mismatch in type information * will cause this message to render without argument substitution. * * @param object from which the message will be created * @param arguments for message * @return a message object that will render the same in all locales; * null if object is null */ static public Message fromObject(Object object, Object... arguments) { Message message = null; if (object != null) { CharSequence cs = object.toString(); message = raw(cs, arguments); } return message; } /** * Returns the string representation of the message in the default locale. * @param message to stringify * @return String representation of of message of null if * message is null */ static public String toString(Message message) { return message != null ? message.toString() : null; } /** Descriptor of this message. */ protected MessageDescriptor descriptor; /** Values used to replace argument specifiers in the format string. */ protected Object[] args; /** * Gets the string representation of this message. * @return String representation of this message */ public String toString() { return toString(Locale.getDefault()); } /** * Gets the string representation of this message appropriate for * locale. * @param locale for which the string representation * will be returned * @return String representation of this message */ public String toString(Locale locale) { String s; String fmt = descriptor.getFormatString(locale); if (needsFormatting(fmt)) { try { s = new Formatter(locale).format(locale, fmt, args).toString(); } catch (IllegalFormatException e) { // This should not happend with any of our internal messages. // However, this may happen for raw messages that have a // mismatch between argument specifier type and argument type. s = fmt; } } else { s = fmt; } if (s == null) s = ""; return s; } /** * Gets the descriptor that holds descriptive information * about this message. * @return MessageDescriptor information */ public MessageDescriptor getDescriptor() { return this.descriptor; } /** * Returns the length of this message as rendered using the default * locale. * * @return the number of chars in this message */ public int length() { return length(Locale.getDefault()); } /** * Returns the byte representation of this messages in the default * locale. * * @return bytes for this message */ public byte[] getBytes() { return toString().getBytes(); } /** * Returns the char value at the specified index of * this message rendered using the default locale. * * @param index the index of the char value to be returned * * @return the specified char value * * @throws IndexOutOfBoundsException * if the index argument is negative or not less than * length() */ public char charAt(int index) throws IndexOutOfBoundsException { return charAt(Locale.getDefault(), index); } /** * Returns a new CharSequence that is a subsequence * of this message rendered using the default locale. * The subsequence starts with the char * value at the specified index and ends with the char * value at index end - 1. The length (in chars) * of the returned sequence is end - start, so if * 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 start or end are negative, * if end is greater than length(), * or if start is greater than end */ public CharSequence subSequence(int start, int end) throws IndexOutOfBoundsException { return subSequence(Locale.getDefault(), start, end); } /** * Returns the length of this message as rendered using a specific * locale. * * @param locale for which the rendering of this message will be * used in determining the length * @return the number of chars in this message */ public int length(Locale locale) { return toString(locale).length(); } /** * Returns the char value at the specified index of * this message rendered using a specific. * * @param locale for which the rendering of this message will be * used in determining the character * @param index the index of the char value to be returned * * @return the specified char value * * @throws IndexOutOfBoundsException * if the index argument is negative or not less than * length() */ public char charAt(Locale locale, int index) throws IndexOutOfBoundsException { return toString(locale).charAt(index); } /** * Returns a new CharSequence that is a subsequence * of this message rendered using a specific locale. * The subsequence starts with the char * value at the specified index and ends with the char * value at index end - 1. The length (in chars) * of the returned sequence is end - start, so if * start == end 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 start or end are negative, * if end is greater than length(), * or if start is greater than end */ public CharSequence subSequence(Locale locale, int start, int end) throws IndexOutOfBoundsException { return toString(locale).subSequence(start, end); } /** * {@inheritDoc} */ public void formatTo(Formatter formatter, int flags, int width, int precision) { // Ignores flags, width and precission for now. // see javadoc for Formattable Locale l = formatter.locale(); formatter.format(l, descriptor.getFormatString(l), args); } /** * Creates a parameterized instance. See the class header * for instructions on how to create messages outside this * package. * @param descriptor for this message * @param args arguments for replacing specifiers in the * message's format string */ Message(MessageDescriptor descriptor, Object... args) { this.descriptor = descriptor; this.args = args; } /** * {@inheritDoc} */ public int compareTo(Object o) { Message thatMessage = (Message)o; return toString().compareTo(thatMessage.toString()); } /** * {@inheritDoc} */ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Message message = (Message) o; return toString().equals(message.toString()); } /** * {@inheritDoc} */ public int hashCode() { int result; result = 31 * toString().hashCode(); return result; } /** * Indicates whether or not formatting should be applied * to the given format string. Note that a format string * might have literal specifiers (%% or %n for example) that * require formatting but are not replaced by arguments. * @param s candiate for formatting * @return boolean where true indicates that the format * string requires formatting */ protected boolean needsFormatting(String s) { return s != null && ((args != null && args.length > 0) || s.matches(".*%[n|%].*")); // match Formatter literals } }