Java tutorial
/* * Copyright 2015 Trento Rise (trentorise.eu) * * Licensed 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 eu.trentorise.opendata.commons; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.Multimap; import java.io.Serializable; import java.util.Arrays; import java.util.List; import java.util.Locale; import javax.annotation.ParametersAreNonnullByDefault; import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.NotThreadSafe; /** * Represents a dictionary of a string that may have translations in several * languages. There can also be multiple strings for the same language, in this * case the first one is the preferred. Never pass null objects to methods, use * empty objects (such as empty String "", unknown Locale {@link Locale#ROOT}, * etc...) instead. * * <p> * To create {@code Dict} instances use {@code of(...)} methods. * </p> * * @author David Leoni <david.leoni@unitn.it> */ @ParametersAreNonnullByDefault @Immutable public final class Dict implements Serializable { private static final long serialVersionUID = 1L; private static final Dict INSTANCE = new Dict(); private static final int PADDING = 10; private ImmutableListMultimap<Locale, String> strings; private Dict() { strings = ImmutableListMultimap.of(); } /** * Creates an empty dictionary */ public static Dict of() { return INSTANCE; } /** * Constructs a Dict with with the provided string(s) in the given locale * * @param strings * a non-null string * @param locale * if locale is unknown use {@link Locale#ROOT} */ public static Dict of(Locale locale, String... strings) { Dict ret = new Dict(); ret.strings = ImmutableListMultimap.<Locale, String>builder().putAll(locale, strings).build(); return ret; } /** * Returns the underlying multimap dictionary. */ public ImmutableListMultimap<Locale, String> asMultimap() { return strings; } /** * Constructs a Dict with with the provided string(s). Strings will be under * unknown locale {@link Locale#ROOT}. * * @param strings * non-null strings */ public static Dict of(String... strings) { return of(Locale.ROOT, strings); } /** * Constructs a Dict with the provided strings. Strings will be under * unknown locale {@link Locale#ROOT} * * @param strings * a non-null list of non-null strings */ public static Dict of(Iterable<String> strings) { return of(Locale.ROOT, strings); } /** * Constructs a Dict with the strings in the provided locale * * @param strings * a non-null list of non-null strings * @param locale * if locale is unknown use {@link Locale#ROOT} */ public static Dict of(Locale locale, Iterable<String> strings) { Dict ret = new Dict(); ret.strings = ImmutableListMultimap.<Locale, String>builder().putAll(locale, strings).build(); return ret; } /** * Constructs a Dict with the provided localized string */ public static Dict of(LocalizedString localizedString) { return Dict.of(localizedString.loc(), localizedString.str()); } private Dict(Builder dictBuilder) { this.strings = dictBuilder.stringsBuilder.build(); } /** * Gets the translations in the given locale. * * @param locale * the language of the desired translations * @return the strings in the given locale if present. If no string is * present an empty list is returned. * * @see #get(java.util.Locale) * @since 1.1 */ public ImmutableList<String> get(Locale locale) { return strings.get(locale); } /** * Gets the translations in the given locale. * * @deprecated use {@link #get(java.util.Locale) instead} * * @param locale * the language of the desired translations * @return the strings in the given locale if present. If no string is * present an empty list is returned. * * @see #string(java.util.Locale) */ public ImmutableList<String> strings(Locale locale) { return strings.get(locale); } /** * Gets the first translation in the given locale. Superceeds * {@link #string(java.util.Locale)} * * @param locale * the language of the desired translation * @return the string in the given locale if present. If no string is * present the empty string is returned. * * @since 1.1 */ public String str(Locale locale) { List<String> rets = strings(locale); if (rets.isEmpty()) { return ""; } else { return rets.get(0); } } /** * Gets the first translation in the given locale. * * @deprecated use {@link #str(java.util.Locale) instead} * * @param locale * the language of the desired translation * @return the string in the given locale if present. If no string is * present the empty string is returned. */ public String string(Locale locale) { return str(locale); } /** * Returns true if there is at least one non-empty translation, otherwise * returns false */ public boolean isEmpty() { for (Locale locale : locales()) { String t = nonEmptyString(locale); if (!t.isEmpty()) { return false; } } return true; } /** * Gets the locales for which translations are present in the dict * * @return the available locales */ public ImmutableSet<Locale> locales() { return strings.keySet(); } /** * Checks if provided text is contained in any of the provided translations. * Both text and translations to check are lowercased according to their * locale. * * @param text * the text to search for * @return true if text is contained in any of the translations, false * otherwise */ public boolean contains(String text) { Preconditions.checkNotNull(text); for (Locale loc : locales()) { String lowText = text.toLowerCase(loc); for (String t : strings.get(loc)) { if (t.toLowerCase(loc).contains(lowText)) { return true; } } } return false; } /** * Returns the first non empty string in the given locale. If it can't find * it, an empty string is returned. * * @param locale * the locale of the desired tranlsation. If unknown, use * {@link Locale#ROOT}. */ public String nonEmptyString(Locale locale) { Preconditions.checkNotNull(locale); for (String s : strings(locale)) { if (!s.isEmpty()) { return s; } } return ""; } /** * * Tries its best to return a meaningful string in one of the provided * languages. * * @return A string in the first available language from the list of * provided locales. If no translation is available, in order, * defaults to English and then whatever it can find in the list of * translations. Empty strings are discarded. If no valid * translation is available at all, returns * {@link LocalizedString#of()}. * * @deprecated use {@link #some(java.lang.Iterable)} instead */ public LocalizedString anyString(Iterable<Locale> locales) { return some(locales); } /** * * Tries its best to return a meaningful string in one of the provided * languages. For more details, see {@link #anyString(java.util.Locale...)} * * Superceeds {@link #anyString(java.util.Locale...)}. * * @since 1.1 */ public LocalizedString some(Locale... locales) { return some(Arrays.asList(locales)); } /** * * Tries its best to return a meaningful string in one of the provided * languages. * * Superceeds {@link #anyString(java.lang.Iterable) }. * * @return A string in the first available language from the list of * provided locales. If no translation is available, in order, * defaults to English and then whatever it can find in the list of * translations. Empty strings are discarded. If no valid * translation is available at all, returns * {@link LocalizedString#of()}. * * @since 1.1 */ public LocalizedString some(Iterable<Locale> locales) { Preconditions.checkNotNull(locales); for (Locale loc : locales) { Preconditions.checkNotNull(loc); String t = nonEmptyString(loc); if (!t.isEmpty()) { return LocalizedString.of(loc, t); } } String t = nonEmptyString(Locale.ENGLISH); if (!t.isEmpty()) { return LocalizedString.of(Locale.ENGLISH, t); } for (Locale loc : locales()) { String other = nonEmptyString(loc); if (!other.isEmpty()) { return LocalizedString.of(loc, other); } } return LocalizedString.of(); } /** * * Tries its best to return a meaningful string in one of the provided * languages. For more details, see {@link #anyString(java.util.Locale...)} * * @deprecated use {@link #some(java.util.Locale...) } instead */ public LocalizedString anyString(Locale... locales) { return some(locales); } /** * * Returns a new dictionary with provided array of strings with the same * locale. Strings will be appended to existing ones for the same locale. * * @param locale * the locale of the strings * @param strings * the strings in the given locale * */ public Dict with(Locale locale, String... strings) { return Dict.builder().put(this).put(locale, strings).build(); } /** * * Returns a new dictionary with provided array of strings with the same * locale unknown locale {@link Locale#ROOT}. Strings will be appended to * existing ones with unknown locale. * * @param strings * the strings in the given locale * */ public Dict with(String... strings) { return Dict.builder().put(this).put(Locale.ROOT, strings).build(); } /** * * Returns a new dictionary with provided array of strings with the same * locale. Strings will be appended to existing ones for the same locale. * * @param locale * the locale of the strings * @param strings * the strings in the given locale * */ public Dict with(Locale locale, Iterable<String> strings) { return Dict.builder().put(this).put(locale, strings).build(); } /** * * Returns a new dictionary which is the result of merging this dictionary * with the provided one. New locales and strings follow any existing * locales and strings. * */ public Dict with(Dict dict) { return Dict.builder().put(this).put(dict).build(); } /** * Returns a string with msg padded with white spaces from the left until * maxLength is reached * * @param msg * the message to pad with spaces * @param maxLength * length after which msg is truncated * @return the padded msg */ private static String padLeft(String msg, int maxLength) { Preconditions.checkNotNull(msg); String nmot; if (msg.length() > maxLength) { nmot = msg.substring(0, msg.length() - 3) + "..."; } else { nmot = String.format("%" + maxLength + "s", msg); } return nmot; } /** * Builds instances of {@link Dict}. Initialized attributes and then invoke * {@link #build()} method to create immutable instance. * <p> * <em>Builder is not thread safe and generally should not be stored in * field or collection, but used immediately to create instances.</em> */ @NotThreadSafe public static final class Builder { private Builder() { } private ImmutableListMultimap.Builder<Locale, String> stringsBuilder = ImmutableListMultimap .<Locale, String>builder(); /** * Stores an array of values with the same locale in the built * dictionary. Strings will be appended to existing ones for the same * locale. * * @param locale * the locale of the string * @param strings * the string in the given locale * @return {@code this} builder for chained invocation */ public Builder put(Locale locale, String... strings) { stringsBuilder.putAll(locale, strings); return this; } /** * Returns an immutable dictionary. */ public Dict build() { return new Dict(this); } /** * Stores a collection of values with the same locale in the built * dictionary. Strings will be appended to existing ones for the same * locale. * * @param locale * the locale of the string * @param strings * strings in the given locale * @return {@code this} builder for chained invocation */ public Builder put(Locale locale, Iterable<String> strings) { stringsBuilder.putAll(locale, strings); return this; } /** * Stores a collection of values with default locale {@link Locale#ROOT} * in the built dictionary. Strings will be appended to existing ones * for the same default locale. * * @return {@code this} builder for chained invocation */ public Builder put(String... strings) { stringsBuilder.putAll(Locale.ROOT, strings); return this; } /** * Stores another dictionary's entries in the built dictionary. New * locales and strings follow any existing locales and strings. */ public Builder put(Dict dict) { stringsBuilder.putAll(dict.strings); return this; } } @Override public int hashCode() { int hash = 7; hash = 53 * hash + (this.strings != null ? this.strings.hashCode() : 0); return hash; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Dict other = (Dict) obj; if (this.strings != other.strings && (this.strings == null || !this.strings.equals(other.strings))) { return false; } return true; } /** * Creates builder for {@link Dict}. * * @return new Dict builder */ public static Dict.Builder builder() { return new Dict.Builder(); } /** * Returns all the strings in the dictionary in a nicely formatted way. */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("\n"); sb.append("{\n"); for (Locale loc : strings.keySet()) { sb.append(padLeft(loc.toString(), PADDING)).append(": ["); boolean first = true; for (String t : strings.get(loc)) { if (first) { first = false; } else { sb.append(", "); } sb.append(t); } sb.append("]\n"); } sb.append("}\n"); sb.append("\n"); return sb.toString(); } /** * Returns a Dict with a copy of a Guava Multimap */ public static Dict of(Multimap<Locale, String> multimap) { Dict ret = new Dict(); ret.strings = ImmutableListMultimap.<Locale, String>copyOf(multimap); return ret; } /** * Returns a new dictionary by merging all the provided dicts together */ public static Dict ofDicts(Dict... dicts) { return ofDicts(Arrays.asList(dicts)); } /** * Returns a new dictionary by merging all the provided dicts together */ public static Dict ofDicts(Iterable<Dict> dicts) { Dict.Builder retb = Dict.builder(); for (Dict st : dicts) { retb.put(st); } return retb.build(); } /** * Returns a dictionary made out of the provided localized strings */ public static Dict ofLocalizedStrings(Iterable<LocalizedString> localizedStrings) { Dict.Builder dictb = Dict.builder(); for (LocalizedString st : localizedStrings) { dictb.put(st.loc(), st.str()); } return dictb.build(); } /** * Returns the dictionary as a list of localized strings */ public ImmutableList<LocalizedString> asLocalizedStrings() { ImmutableList.Builder<LocalizedString> retb = ImmutableList.builder(); for (Locale locale : locales()) { for (String s : get(locale)) { retb.add(LocalizedString.of(locale, s)); } } return retb.build(); } }