<commit_before>/*
 * Copyright (c) 2009-2010, <NAME> & <NAME>
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 *  * Neither the name of JSR-310 nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package javax.time.calendar.zone;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.time.CalendricalException;
import javax.time.calendar.OffsetDateTime;

/**
 * A group of time-zone rules wrapping a provider of multiple versions of the data.
 * <p>
 * Zone rule data is provided by organizations or groups.
 * To manage this data each group is given a unique ID.
 * One group is provided as standard - 'TZDB' - and more may be added.
 * <p>
 * The 'TZDB' group represents that data provided by the
 * <a href="http://www.twinsun.com/tz/tz-link.htm">time-zone database</a>
 * as used in older versions of Java and many operating systems.
 * <p>
 * Other groups of zone rules can be developed and registered.
 * Group IDs should be reverse domain names as with package names unless explicitly
 * approved by the JSR-310 expert group.
 * <p>
 * ZoneRulesGroup is thread-safe and immutable.
 * <p>
 * The static methods of ZoneRulesGroup wrap a thread-safe map of groups.
 * New groups and providers may safely be added during the lifetime of the application.
 * To maintain data integrity, providers may not be removed during the lifetime of the application.
 *
 * @author <NAME>
 */
public final class ZoneRulesGroup {

    /**
     * The zone IDs.
     * Should not be empty.
     */
    private static final Set<String> IDS = new CopyOnWriteArraySet<String>();
    /**
     * The zone rule groups.
     * Should not be empty.
     */
    private static final ConcurrentMap<String, ZoneRulesGroup> GROUPS =
            new ConcurrentHashMap<String, ZoneRulesGroup>(16, 0.75f, 2);

    static {
        JarZoneRulesDataProvider.load();
    }

    /**
     * The zone rules group ID, such as 'TZDB'.
     */
    private final String groupID;
    /**
     * The zone rule data.
     * The TreeMap must never be visibly empty.
     */
    private final ConcurrentMap<String, TreeMap<String, ZoneRulesDataProvider>> regions =
            new ConcurrentHashMap<String, TreeMap<String, ZoneRulesDataProvider>>(100, 0.75f, 2);

    //-----------------------------------------------------------------------
    /**
     * Checks if the group ID is valid.
     * <p>
     * Which groups are available is dependent on the registered providers.
     *
     * @param groupID  the group ID, null returns false
     * @return true if the group ID is valid
     */
    public static boolean isValidGroup(String groupID) {
        if (groupID == null) {
            return false;
        }
        return GROUPS.containsKey(groupID);
    }

    /**
     * Gets a group by ID, such as 'TZDB'.
     * <p>
     * Which groups are available is dependent on the registered providers.
     *
     * @param groupID  the group ID, not null
     * @return the zone rules group, never null
     * @throws CalendricalException if the group ID is not found
     */
    public static ZoneRulesGroup getGroup(String groupID) {
        ZoneRules.checkNotNull(groupID, "Group ID must not be null");
        ZoneRulesGroup group = GROUPS.get(groupID);
        if (group == null) {
            throw new CalendricalException("Unknown time zone group ID: " + groupID);
        }
        return group;
    }

    /**
     * Gets the set of available zone rule groups.
     * <p>
     * Which groups are available is dependent on the registered providers.
     * <p>
     * The returned groups will remain available and valid for the lifetime of the application as
     * there is no way to deregister time-zone information. More groups may be added during
     * the lifetime of the application, however the returned list will not be altered.
     *
     * @return an unsorted, independent, modifiable list of available groups, never null
     */
    public static List<ZoneRulesGroup> getAvailableGroups() {
        return new ArrayList<ZoneRulesGroup>(GROUPS.values());
    }

    /**
     * Gets a view of the complete set of parsable time-zone IDs.
     * <p>
     * This returns the complete set of IDs that can be parsed.
     * For each group and region, all the valid versions and the 'floating'
     * version IDs will be included. Each 'TZDB' ID will be included twice
     * as the 'TZDB:' prefix is optional in parsing.
     * For more detailed control, use the instance methods on this class.
     * <p>
     * For example, for the single time-zone of 'Europe/London' and two available
     * versions, the set would contain:<commit_msg>Better error message when TZDB jar file missing<commit_after>/*
 * Copyright (c) 2009-2010, <NAME> & <NAME>
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 *  * Neither the name of JSR-310 nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package javax.time.calendar.zone;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.time.CalendricalException;
import javax.time.calendar.OffsetDateTime;

/**
 * A group of time-zone rules wrapping a provider of multiple versions of the data.
 * <p>
 * Zone rule data is provided by organizations or groups.
 * To manage this data each group is given a unique ID.
 * One group is provided as standard - 'TZDB' - and more may be added.
 * <p>
 * The 'TZDB' group represents that data provided by the
 * <a href="http://www.twinsun.com/tz/tz-link.htm">time-zone database</a>
 * as used in older versions of Java and many operating systems.
 * <p>
 * Other groups of zone rules can be developed and registered.
 * Group IDs should be reverse domain names as with package names unless explicitly
 * approved by the JSR-310 expert group.
 * <p>
 * ZoneRulesGroup is thread-safe and immutable.
 * <p>
 * The static methods of ZoneRulesGroup wrap a thread-safe map of groups.
 * New groups and providers may safely be added during the lifetime of the application.
 * To maintain data integrity, providers may not be removed during the lifetime of the application.
 *
 * @author <NAME>
 */
public final class ZoneRulesGroup {

    /**
     * The zone IDs.
     * Should not be empty.
     */
    private static final Set<String> IDS = new CopyOnWriteArraySet<String>();
    /**
     * The zone rule groups.
     * Should not be empty.
     */
    private static final ConcurrentMap<String, ZoneRulesGroup> GROUPS =
            new ConcurrentHashMap<String, ZoneRulesGroup>(16, 0.75f, 2);

    static {
        JarZoneRulesDataProvider.load();
    }

    /**
     * The zone rules group ID, such as 'TZDB'.
     */
    private final String groupID;
    /**
     * The zone rule data.
     * The TreeMap must never be visibly empty.
     */
    private final ConcurrentMap<String, TreeMap<String, ZoneRulesDataProvider>> regions =
            new ConcurrentHashMap<String, TreeMap<String, ZoneRulesDataProvider>>(100, 0.75f, 2);

    //-----------------------------------------------------------------------
    /**
     * Checks if the group ID is valid.
     * <p>
     * Which groups are available is dependent on the registered providers.
     *
     * @param groupID  the group ID, null returns false
     * @return true if the group ID is valid
     */
    public static boolean isValidGroup(String groupID) {
        if (groupID == null) {
            return false;
        }
        return GROUPS.containsKey(groupID);
    }

    /**
     * Gets a group by ID, such as 'TZDB'.
     * <p>
     * Which groups are available is dependent on the registered providers.
     * <p>
     * This method relies on time-zone data provider files. These are often loaded as jar files.
     * If no providers have been {@link #registerProvider0(ZoneRulesDataProvider) registered} or no
     * provider has been registered for the requested group then an exception is thrown.
     *
     * @param groupID  the group ID, not null
     * @return the zone rules group, never null
     * @throws CalendricalException if the group ID is not found
     */
    public static ZoneRulesGroup getGroup(String groupID) {
        ZoneRules.checkNotNull(groupID, "Group ID must not be null");
        ZoneRulesGroup group = GROUPS.get(groupID);
        if (group == null) {
            if (GROUPS.isEmpty()) {
                throw new CalendricalException("Unknown time-zone group ID '" + groupID + "', no time-zone data files registered");
            }
            throw new CalendricalException("Unknown time-zone group ID '" + groupID + '\'');
        }
        return group;
    }

    /**
     * Gets the set of available zone rule groups.
     * <p>
     * Which groups are available is dependent on the registered providers.
     * <p>
     * The returned groups will remain available and valid for the lifetime of the application as
     * there is no way to deregister time-zone information. More groups may be added during
     * the lifetime of the application, however the returned list will not be altered.
     *
     * @return an unsorted, independent, modifiable list of available groups, never null
     */
    public static List<ZoneRulesGroup> getAvailableGroups() {
        return new ArrayList<ZoneRulesGroup>(GROUPS.values());
    }

    /**
     * Gets a view of the complete set of parsable time-zone IDs.
     * <p>
     * This returns the complete set of IDs that can be parsed.
     * For each group and region, all the valid versions and the 'floating'
     * version IDs will be included. Each 'TZDB' ID will be included twice
     * as the 'TZDB:' prefix is optional in parsing.
     * For more detailed control, use the instance methods on this class.
     * <p>
     * For example, for the single time-zone of 'Europe/London' and two available
     * versions, the set would contain:
{'fraction_non_alphanumeric': 0.08488407565588774, 'fraction_numerical': 0.0041946308724832215, 'mean_word_length': 2.9295774647887325, 'pattern_counts': {'":': 0, '<': 56, '<?xml version=': 0, '>': 56, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4041526', 'n_tokens_mistral': 3528, 'n_tokens_neox': 3339, 'n_words': 1556}