All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sellcom.geotemporal.geography.GeoRegion Maven / Gradle / Ivy

/*
 * Copyright (c) 2015-2017 Petr Zelenka .
 *
 * 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 org.sellcom.geotemporal.geography;

import java.util.Locale;
import java.util.Objects;
import java.util.StringTokenizer;

import org.sellcom.geotemporal.internal.Contract;
import org.sellcom.geotemporal.internal.StringUtils;

/**
 * Representation of a specific geographical or political region.
 *
 * 

Structure of a GeoRegion

* Every region is defined by a country code and up to five codes of administrative units at different subdivision levels. * The overall region code is composed of the individual codes separated by a comma. *

* Regions are strictly hierarchical, however the administrative units at some subdivision levels may be skipped. * This situation can be found in Germany, for instance, where the subdivision level of "governmental districts" (orig. "Regierungsbezirke") is defined only in some of the "federal states" (orig. "Bundesländer"). * *

Predefined GeoRegions

* Region codes defined by this library can be found in the {@link StandardGeoRegions} class and its inner classes. * Custom-defined region codes shall be compatible with these codes. * *

Default GeoRegion

* Similarly to {@link Locale}, this class defines a default region. * The default region is set at class loading time from the {@code "user.georegion"} system property. * If the property is not defined or if its value is empty, a qualified guess of the default region is made from the default locale. * If the qualified guess fails, a {@link GeoRegionException} is thrown. * The default region can be changed at any time using the {@link #setDefault(GeoRegion)} method. * * @since 1.0 */ public class GeoRegion { /** * The root region. * * @since 1.0 */ public static final GeoRegion ROOT = new GeoRegion(""); private static GeoRegion defaultGeoRegion; static { initDefault(); } private final String countryCode; private final String level1SubdivisionCode; private final String level2SubdivisionCode; private final String level3SubdivisionCode; private final String level4SubdivisionCode; private final String level5SubdivisionCode; /** * Creates a region for the given country code. * * @throws IllegalArgumentException if {@code countryCode} is {@code null} * * @since 1.0 */ public GeoRegion(String countryCode) { this(countryCode, "", "", "", "", ""); } /** * Creates a region for the given country code and the codes at the 1st subdivision level. * * @throws IllegalArgumentException if {@code countryCode} is {@code null} * @throws IllegalArgumentException if {@code level1SubdivisionCode} is {@code null} * * @since 1.0 */ public GeoRegion(String countryCode, String level1SubdivisionCode) { this(countryCode, level1SubdivisionCode, "", "", "", ""); } /** * Creates a region for the given country code and the codes at the 1st and 2nd subdivision levels. * * @throws IllegalArgumentException if {@code countryCode} is {@code null} * @throws IllegalArgumentException if {@code level1SubdivisionCode} is {@code null} * @throws IllegalArgumentException if {@code level2SubdivisionCode} is {@code null} * * @since 1.0 */ public GeoRegion(String countryCode, String level1SubdivisionCode, String level2SubdivisionCode) { this(countryCode, level1SubdivisionCode, level2SubdivisionCode, "", "", ""); } /** * Creates a region for the given country code and the codes at the 1st to 3rd subdivision levels. * * @throws IllegalArgumentException if {@code countryCode} is {@code null} * @throws IllegalArgumentException if {@code level1SubdivisionCode} is {@code null} * @throws IllegalArgumentException if {@code level2SubdivisionCode} is {@code null} * @throws IllegalArgumentException if {@code level3SubdivisionCode} is {@code null} * * @since 1.0 */ public GeoRegion(String countryCode, String level1SubdivisionCode, String level2SubdivisionCode, String level3SubdivisionCode) { this(countryCode, level1SubdivisionCode, level2SubdivisionCode, level3SubdivisionCode, "", ""); } /** * Creates a region for the given country code and the codes at the 1st to 4th subdivision levels. * * @throws IllegalArgumentException if {@code countryCode} is {@code null} * @throws IllegalArgumentException if {@code level1SubdivisionCode} is {@code null} * @throws IllegalArgumentException if {@code level2SubdivisionCode} is {@code null} * @throws IllegalArgumentException if {@code level3SubdivisionCode} is {@code null} * @throws IllegalArgumentException if {@code level4SubdivisionCode} is {@code null} * * @since 1.0 */ public GeoRegion(String countryCode, String level1SubdivisionCode, String level2SubdivisionCode, String level3SubdivisionCode, String level4SubdivisionCode) { this(countryCode, level1SubdivisionCode, level2SubdivisionCode, level3SubdivisionCode, level4SubdivisionCode, ""); } /** * Creates a region for the given country code and the codes at the 1st to 5th subdivision levels. * * @throws IllegalArgumentException if {@code countryCode} is {@code null} * @throws IllegalArgumentException if {@code level1SubdivisionCode} is {@code null} * @throws IllegalArgumentException if {@code level2SubdivisionCode} is {@code null} * @throws IllegalArgumentException if {@code level3SubdivisionCode} is {@code null} * @throws IllegalArgumentException if {@code level4SubdivisionCode} is {@code null} * @throws IllegalArgumentException if {@code level5SubdivisionCode} is {@code null} * * @since 1.0 */ public GeoRegion(String countryCode, String level1SubdivisionCode, String level2SubdivisionCode, String level3SubdivisionCode, String level4SubdivisionCode, String level5SubdivisionCode) { Contract.checkArgument(countryCode != null, "Country code must not be null"); Contract.checkArgument(level1SubdivisionCode != null, "Subdivision code at level 1 must not be null"); Contract.checkArgument(level2SubdivisionCode != null, "Subdivision code at level 2 must not be null"); Contract.checkArgument(level3SubdivisionCode != null, "Subdivision code at level 3 must not be null"); Contract.checkArgument(level4SubdivisionCode != null, "Subdivision code at level 4 must not be null"); Contract.checkArgument(level5SubdivisionCode != null, "Subdivision code at level 5 must not be null"); this.countryCode = countryCode; this.level1SubdivisionCode = level1SubdivisionCode; this.level2SubdivisionCode = level2SubdivisionCode; this.level3SubdivisionCode = level3SubdivisionCode; this.level4SubdivisionCode = level4SubdivisionCode; this.level5SubdivisionCode = level5SubdivisionCode; } /** * Returns this region limited to the given administrative level. * * @throws IllegalArgumentException if {@code level} is negative * @throws IllegalArgumentException if {@code level} is greater than 5 * * @since 1.0 */ public GeoRegion atLevel(int level) { Contract.checkArgument(level >= 0, "Level must not be negative: {0}", level); switch (level) { case 0: { return new GeoRegion(countryCode); } case 1: { return new GeoRegion(countryCode, level1SubdivisionCode); } case 2: { return new GeoRegion(countryCode, level1SubdivisionCode, level2SubdivisionCode); } case 3: { return new GeoRegion(countryCode, level1SubdivisionCode, level2SubdivisionCode, level3SubdivisionCode); } case 4: { return new GeoRegion(countryCode, level1SubdivisionCode, level2SubdivisionCode, level3SubdivisionCode, level4SubdivisionCode); } case 5: { return new GeoRegion(countryCode, level1SubdivisionCode, level2SubdivisionCode, level3SubdivisionCode, level4SubdivisionCode, level5SubdivisionCode); } default: { throw new IllegalArgumentException(String.format("Invalid level: %d", level)); } } } @Override public boolean equals(Object other) { if (other == this) { return true; } if (other instanceof GeoRegion) { GeoRegion otherCast = (GeoRegion) other; return Objects.equals(countryCode, otherCast.countryCode) && Objects.equals(level1SubdivisionCode, otherCast.level1SubdivisionCode) && Objects.equals(level2SubdivisionCode, otherCast.level2SubdivisionCode) && Objects.equals(level3SubdivisionCode, otherCast.level3SubdivisionCode) && Objects.equals(level4SubdivisionCode, otherCast.level4SubdivisionCode) && Objects.equals(level5SubdivisionCode, otherCast.level5SubdivisionCode); } return false; } /** * Return the overall code of this region. * * @since 1.0 */ public String getCode() { if (!level5SubdivisionCode.isEmpty()) { return String.join(",", countryCode, level1SubdivisionCode, level2SubdivisionCode, level3SubdivisionCode, level4SubdivisionCode, level5SubdivisionCode); } else { if (!level4SubdivisionCode.isEmpty()) { return String.join(",", countryCode, level1SubdivisionCode, level2SubdivisionCode, level3SubdivisionCode, level4SubdivisionCode); } else { if (!level3SubdivisionCode.isEmpty()) { return String.join(",", countryCode, level1SubdivisionCode, level2SubdivisionCode, level3SubdivisionCode); } else { if (!level2SubdivisionCode.isEmpty()) { return String.join(",", countryCode, level1SubdivisionCode, level2SubdivisionCode); } else { if (!level1SubdivisionCode.isEmpty()) { return String.join(",", countryCode, level1SubdivisionCode); } else { return countryCode; } } } } } } /** * Return the country code of this region. * * @since 1.0 */ public String getCountryCode() { return countryCode; } /** * Returns the default region. * * @since 1.0 */ public static GeoRegion getDefault() { return defaultGeoRegion; } /** * Returns the immediate parent of this region. * * @since 1.0 */ public GeoRegion getParent() { if (!StringUtils.isNullOrEmpty(level5SubdivisionCode)) { return new GeoRegion(countryCode, level1SubdivisionCode, level2SubdivisionCode, level3SubdivisionCode, level4SubdivisionCode); } if (!StringUtils.isNullOrEmpty(level4SubdivisionCode)) { return new GeoRegion(countryCode, level1SubdivisionCode, level2SubdivisionCode, level3SubdivisionCode); } if (!StringUtils.isNullOrEmpty(level3SubdivisionCode)) { return new GeoRegion(countryCode, level1SubdivisionCode, level2SubdivisionCode); } if (!StringUtils.isNullOrEmpty(level2SubdivisionCode)) { return new GeoRegion(countryCode, level1SubdivisionCode); } if (!StringUtils.isNullOrEmpty(level1SubdivisionCode)) { return new GeoRegion(countryCode); } return ROOT; } @Override public int hashCode() { return Objects.hash( countryCode, level1SubdivisionCode, level2SubdivisionCode, level3SubdivisionCode, level4SubdivisionCode, level5SubdivisionCode); } /** * Returns a region corresponding to the given code. * * @throws IllegalArgumentException if {@code code} is {@code null} * @throws GeoRegionParseException if the region code cannot be parsed * * @since 1.0 */ public static GeoRegion parse(String code) { Contract.checkArgument(code != null, "Region code must not be null"); StringTokenizer tokenizer = new StringTokenizer(code, ","); switch (tokenizer.countTokens()) { case 1: { return new GeoRegion(tokenizer.nextToken()); } case 2: { return new GeoRegion(tokenizer.nextToken(), tokenizer.nextToken()); } case 3: { return new GeoRegion(tokenizer.nextToken(), tokenizer.nextToken(), tokenizer.nextToken()); } case 4: { return new GeoRegion(tokenizer.nextToken(), tokenizer.nextToken(), tokenizer.nextToken(), tokenizer.nextToken()); } case 5: { return new GeoRegion(tokenizer.nextToken(), tokenizer.nextToken(), tokenizer.nextToken(), tokenizer.nextToken(), tokenizer.nextToken()); } case 6: { return new GeoRegion(tokenizer.nextToken(), tokenizer.nextToken(), tokenizer.nextToken(), tokenizer.nextToken(), tokenizer.nextToken(), tokenizer.nextToken()); } default: { throw new GeoRegionParseException(String.format("Invalid code: %s", code)); } } } /** * Sets the default region. * * @since 1.0 */ public static void setDefault(GeoRegion region) { Contract.checkArgument(region != null, "Region must not be null"); defaultGeoRegion = region; } @Override public String toString() { return getCode(); } private static void initDefault() { String regionCode; regionCode = System.getProperty("user.georegion"); if (!StringUtils.isNullOrEmpty(regionCode)) { defaultGeoRegion = parse(regionCode); return; } regionCode = GeoRegionUtils.regionCodeFromCountry(); if (!StringUtils.isNullOrEmpty(regionCode)) { defaultGeoRegion = parse(regionCode); return; } regionCode = GeoRegionUtils.regionCodeFromLanguage(); if (!StringUtils.isNullOrEmpty(regionCode)) { defaultGeoRegion = parse(regionCode); return; } throw new GeoRegionException("Cannot determine default GeoRegion. Please set the 'user.georegion' system property."); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy