blob: f2e333b3a9ec6a68f0435fb292f30ebbf3fd25ba [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.sis.util.iso;
import java.util.Locale;
import java.util.Formatter;
import java.util.Formattable;
import java.util.FormattableFlags;
import org.opengis.util.InternationalString;
import org.apache.sis.internal.util.Strings;
import org.apache.sis.util.CharSequences;
/**
* Base class for character strings that has been internationalized into several locales.
* The {@link InternationalString} interface is used instead of the {@link String} class
* whenever an attribute needs to be internationalization capable.
*
* <p>The default value (as returned by {@link #toString()} and other {@link CharSequence} methods)
* is the string in the current {@linkplain Locale#getDefault() system-wide default locale}.
* The {@linkplain Comparable natural ordering} is defined by the value returned by {@link #toString()}.</p>
*
* <div class="section">Substituting a free text by a code list</div>
* The ISO standard allows to substitute some character strings in the <cite>"free text"</cite> domain
* by a {@link org.opengis.util.CodeList} value. This can be done with:
*
* <ul>
* <li>{@link Types#getCodeTitle(CodeList)} for getting the {@link InternationalString}
* instance to store in a metadata property.</li>
* <li>{@link Types#forCodeTitle(CharSequence)} for retrieving the {@link org.opengis.util.CodeList}
* previously stored as an {@code InternationalString}.</li>
* </ul>
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @version 0.3
* @since 0.3
* @module
*/
public abstract class AbstractInternationalString implements InternationalString, Formattable {
/**
* The string in the {@linkplain Locale#getDefault() system default} locale, or {@code null}
* if this string has not yet been determined. This is the default string returned by
* {@link #toString()} and others methods from the {@link CharSequence} interface.
*
* <div class="section">Thread safety</div>
* For thread safety this field shall either be read and written in a synchronized block,
* or be fixed at construction time and never changed after than point. All other usages
* are prohibited.
*
* <div class="section">Serialization</div>
* This field is not serialized because serialization is often used for data transmission
* between a server and a client, and the client may not use the same locale than the server.
* We want the locale to be examined again on the client side.
*/
transient String defaultValue;
/**
* Constructs an international string.
*/
protected AbstractInternationalString() {
}
/**
* Returns the length of the string in the {@linkplain Locale#getDefault() default locale}.
* This is the length of the string returned by {@link #toString()}.
*
* @return length of the string in the default locale.
*/
@Override
public int length() {
return CharSequences.length(toString());
}
/**
* Returns the character of the string in the {@linkplain Locale#getDefault() default locale}
* at the specified index. This is a character of the string returned by {@link #toString()}.
*
* @param index the index of the character.
* @return the character at the specified index.
* @throws IndexOutOfBoundsException if the specified index is out of bounds.
*/
@Override
public char charAt(final int index) throws IndexOutOfBoundsException {
return toString().charAt(index);
}
/**
* Returns a subsequence of the string in the {@linkplain Locale#getDefault() default locale}.
* The subsequence is a {@link String} object starting with the character value at the specified
* index and ending with the character value at index {@code end - 1}.
*
* @param start the start index, inclusive.
* @param end the end index, exclusive.
* @return the specified subsequence.
* @throws IndexOutOfBoundsException if {@code start} or {@code end} is out of range.
*/
@Override
public CharSequence subSequence(final int start, final int end) {
return toString().substring(start, end);
}
/**
* Returns this string in the given locale. If no string is available in the given locale,
* then some fallback locale is used. The fallback locale is implementation-dependent, and
* is not necessarily the same than the default locale used by the {@link #toString()} method.
*
* <div class="section">Handling of <code>Locale.ROOT</code> argument value</div>
* {@link Locale#ROOT} can be given to this method for requesting a "unlocalized" string,
* typically some programmatic values like enumerations or identifiers. While identifiers
* often look like English words, {@code Locale.ROOT} is not considered synonymous to
* {@link Locale#ENGLISH} because the values may differ in the way numbers and dates are
* formatted (e.g. using the ISO 8601 standard for dates instead than English conventions).
*
* <div class="section">Handling of <code>null</code> argument value</div>
* The {@code Locale.ROOT} constant is new in Java 6. Some other libraries designed for Java 5
* use the {@code null} value for "unlocalized" strings. Apache SIS accepts {@code null} value
* for inter-operability with those libraries. However the behavior is implementation dependent:
* some subclasses will take {@code null} as a synonymous of the system default locale, while
* other subclasses will take {@code null} as a synonymous of the root locale. In order to
* ensure determinist behavior, client code are encouraged to specify only non-null values.
*
* @param locale the desired locale for the string to be returned.
* @return the string in the given locale if available, or in an
* implementation-dependent fallback locale otherwise.
*
* @see Locale#getDefault()
* @see Locale#ROOT
*/
@Override
public abstract String toString(Locale locale);
/**
* Returns this string in the default locale. Invoking this method is equivalent to invoking
* <code>{@linkplain #toString(Locale) toString}({@linkplain Locale#getDefault()})</code>.
*
* <p>All methods from {@link CharSequence} operate on this string.
* This string is also used as the criterion for {@linkplain Comparable natural ordering}.</p>
*
* @return the string in the default locale.
*/
@Override
public synchronized String toString() {
String text = defaultValue;
if (text == null) {
text = toString(Locale.getDefault());
if (text == null) {
return "";
}
defaultValue = text;
}
return text;
}
/**
* Formats this international string using the given formatter.
* This method appends the string obtained by:
*
* <blockquote><code>
* {@linkplain #toString(Locale) toString}(formatter.{@linkplain Formatter#locale()})
* </code></blockquote>
*
* @param formatter the formatter to use for formatting this string.
* @param flags a bitmask of {@link FormattableFlags} values.
* @param width the minimum number of characters, or -1 if none.
* @param precision the maximum number of characters (before expanding to the {@code width}),
* or -1 for no restriction.
*/
@Override
public void formatTo(final Formatter formatter, final int flags, final int width, final int precision) {
Strings.formatTo(formatter, flags, width, precision, toString(formatter.locale()));
}
/**
* Compares this string with the specified object for order. This method compares
* the string in the {@linkplain Locale#getDefault() default locale}, as returned
* by {@link #toString()}.
*
* @param object the string to compare with this string.
* @return a negative number if this string is before the given string,
* a positive number if after, or 0 if equals.
*/
@Override
public int compareTo(final InternationalString object) {
return toString().compareTo(object.toString());
}
}