/*
* Copyright (C) 2013 The Android Open Source Project
*
* 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 com.android.tools.idea.rendering;
import com.android.ide.common.resources.LocaleManager;
import com.android.ide.common.resources.configuration.FolderConfiguration;
import com.android.ide.common.resources.configuration.LanguageQualifier;
import com.android.ide.common.resources.configuration.RegionQualifier;
import com.google.common.base.Objects;
import com.intellij.openapi.util.text.StringUtil;
import icons.AndroidIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.Comparator;
import static com.android.ide.common.resources.configuration.LanguageQualifier.FAKE_LANG_VALUE;
import static com.android.ide.common.resources.configuration.RegionQualifier.FAKE_REGION_VALUE;
/**
* A language,region pair
*/
public class Locale {
/**
* A special marker region qualifier representing any region
*/
private static final RegionQualifier ANY_REGION = new RegionQualifier(FAKE_REGION_VALUE);
/**
* A special marker language qualifier representing any language
*/
private static final LanguageQualifier ANY_LANGUAGE = new LanguageQualifier(FAKE_LANG_VALUE);
/**
* A locale which matches any language and region
*/
public static final Locale ANY = new Locale(ANY_LANGUAGE, ANY_REGION);
/**
* The language qualifier, or {@link #ANY_LANGUAGE} if this locale matches any language
*/
@NotNull
public final LanguageQualifier language;
/**
* The language qualifier, or {@link #ANY_REGION} if this locale matches any region
*/
@NotNull
public final RegionQualifier region;
/**
* Constructs a new {@linkplain Locale} matching a given language in a given locale.
*
* @param language the language
* @param region the region
*/
private Locale(@NotNull LanguageQualifier language, @NotNull RegionQualifier region) {
if (language.getValue().equals(FAKE_LANG_VALUE)) {
language = ANY_LANGUAGE;
}
if (region.getValue().equals(FAKE_REGION_VALUE)) {
region = ANY_REGION;
}
this.language = language;
this.region = region;
}
/**
* Constructs a new {@linkplain Locale} matching a given language in a given specific locale.
*
* @param language the language
* @param region the region
* @return a locale with the given language and region
*/
@NotNull
public static Locale create(@NotNull LanguageQualifier language, @Nullable RegionQualifier region) {
return new Locale(language, region != null ? region : ANY_REGION);
}
/**
* Constructs a new {@linkplain Locale} for the given language, matching any regions.
*
* @param language the language
* @return a locale with the given language and region
*/
@NotNull
public static Locale create(@NotNull LanguageQualifier language) {
return new Locale(language, ANY_REGION);
}
/**
* Constructs a new {@linkplain Locale} for the given folder configuration
*
* @param folder the folder configuration
* @return a locale with the given language and region
*/
public static Locale create(FolderConfiguration folder) {
LanguageQualifier language = folder.getLanguageQualifier();
RegionQualifier region = folder.getRegionQualifier();
if (language == null && region == null) {
return ANY;
} else if (region == null) {
return create(language);
} else {
assert language != null;
return create(language, region);
}
}
/**
* Constructs a new {@linkplain Locale} for the given locale string, e.g. "zh" or "en-rUS".
*
* @param localeString the locale description
* @return the corresponding locale
*/
@NotNull
public static Locale create(@NotNull String localeString) {
LanguageQualifier language;
RegionQualifier region;
// Load locale. Note that this can get overwritten by the
// project-wide settings read below.
int index = localeString.indexOf('-');
if (index != -1) {
language = new LanguageQualifier(localeString.substring(0, index));
assert localeString.charAt(index + 1) == 'r' : localeString;
region = new RegionQualifier(localeString.substring(index + 2));
} else {
assert localeString.length() == 2 : localeString;
assert !localeString.equals(LanguageQualifier.FAKE_LANG_VALUE);
language = new LanguageQualifier(localeString);
region = ANY_REGION;
}
return new Locale(language, region);
}
/**
* Returns a flag image to use for this locale
*
* @return a flag image, or a default globe icon
*/
@NotNull
public Icon getFlagImage() {
String languageCode = hasLanguage() ? language.getValue() : null;
String regionCode = hasRegion() ? region.getValue() : null;
FlagManager icons = FlagManager.get();
if (languageCode == null && regionCode == null) {
return AndroidIcons.Globe;
}
else {
Icon image = icons.getFlag(languageCode, regionCode);
if (image == null) {
image = AndroidIcons.EmptyFlag;
}
return image;
}
}
/**
* Returns true if this locale specifies a specific language. This is true
* for all locales except {@link #ANY}.
*
* @return true if this locale specifies a specific language
*/
public boolean hasLanguage() {
return language != ANY_LANGUAGE;
}
/**
* Returns true if this locale specifies a specific region
*
* @return true if this locale specifies a region
*/
public boolean hasRegion() {
return region != ANY_REGION;
}
/**
* Returns the locale formatted as language-region. If region is not set,
* language is returned. If language is not set, empty string is returned.
*/
public String toLocaleId() {
// Return lang-reg only if both lang and reg are present. Else return lang.
return hasLanguage() && hasRegion() ? language.getValue() + "-" + region.getValue() : hasLanguage() ? language.getValue() : "";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + language.hashCode();
result = prime * result + region.hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
Locale other = (Locale)obj;
if (!language.equals(other.language)) return false;
if (!region.equals(other.region)) return false;
return true;
}
@Override
public String toString() {
return Objects.toStringHelper(this).omitNullValues().addValue(language.getValue()).addValue(region.getValue()).toString();
}
/**
* Comparator for comparing locales by language names (and as a secondary key, the region names)
*/
public static final Comparator<Locale> LANGUAGE_NAME_COMPARATOR = new Comparator<Locale>() {
@Override
public int compare(Locale locale1, Locale locale2) {
LanguageQualifier language1 = locale1.language;
LanguageQualifier language2 = locale2.language;
if (language1 == ANY_LANGUAGE) {
return language2 == ANY_LANGUAGE ? 0 : -1;
}
else if (language2 == ANY_LANGUAGE) {
return 1;
}
String name1 = LocaleManager.getLanguageName(language1.getValue());
String name2 = LocaleManager.getLanguageName(language2.getValue());
int compare = StringUtil.compare(name1, name2, false);
if (compare == 0) {
return REGION_NAME_COMPARATOR.compare(locale1, locale2);
}
return compare;
}
};
/**
* Comparator for comparing locales by language ISO codes (and as a secondary key, the region ISO codes)
*/
public static final Comparator<Locale> LANGUAGE_CODE_COMPARATOR = new Comparator<Locale>() {
@Override
public int compare(Locale locale1, Locale locale2) {
LanguageQualifier language1 = locale1.language;
LanguageQualifier language2 = locale2.language;
if (language1 == ANY_LANGUAGE) {
return language2 == ANY_LANGUAGE ? 0 : -1;
}
else if (language2 == ANY_LANGUAGE) {
return 1;
}
String code1 = language1.getValue();
String code2 = language2.getValue();
int compare = StringUtil.compare(code1, code2, false);
if (compare == 0) {
return REGION_CODE_COMPARATOR.compare(locale1, locale2);
}
return compare;
}
};
/**
* Comparator for comparing locales by region names
*/
public static final Comparator<Locale> REGION_NAME_COMPARATOR = new Comparator<Locale>() {
@Override
public int compare(Locale locale1, Locale locale2) {
RegionQualifier region1 = locale1.region;
RegionQualifier region2 = locale2.region;
if (region1 == ANY_REGION) {
return region2 == ANY_REGION ? 0 : -1;
} else if (region2 == ANY_REGION) {
return 1;
}
String language1 = LocaleManager.getRegionName(region1.getValue());
String language2 = LocaleManager.getRegionName(region2.getValue());
return StringUtil.compare(language1, language2, false);
}
};
/**
* Comparator for comparing locales by region ISO codes
*/
public static final Comparator<Locale> REGION_CODE_COMPARATOR = new Comparator<Locale>() {
@Override
public int compare(Locale locale1, Locale locale2) {
RegionQualifier region1 = locale1.region;
RegionQualifier region2 = locale2.region;
if (region1 == ANY_REGION) {
return region2 == ANY_REGION ? 0 : -1;
} else if (region2 == ANY_REGION) {
return 1;
}
String code1 = region1.getValue();
String code2 = region2.getValue();
return StringUtil.compare(code1, code2, false);
}
};
}