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

com.helger.holiday.mgr.XMLHolidayManager Maven / Gradle / Ivy

/**
 * Copyright (C) 2014-2017 Philip Helger (www.helger.com)
 * philip[at]helger[dot]com
 *
 * 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.helger.holiday.mgr;

import java.io.InputStream;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.WillClose;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.helger.commons.CGlobal;
import com.helger.commons.ValueEnforcer;
import com.helger.commons.annotation.Nonempty;
import com.helger.commons.annotation.ReturnsMutableCopy;
import com.helger.commons.collection.ArrayHelper;
import com.helger.commons.collection.impl.CommonsArrayList;
import com.helger.commons.collection.impl.CommonsHashSet;
import com.helger.commons.collection.impl.ICommonsList;
import com.helger.commons.collection.impl.ICommonsSet;
import com.helger.commons.io.resource.ClassPathResource;
import com.helger.commons.io.stream.StreamHelper;
import com.helger.commons.lang.GenericReflection;
import com.helger.commons.locale.country.ECountry;
import com.helger.holiday.HolidayMap;
import com.helger.holiday.jaxb.Configuration;
import com.helger.holiday.jaxb.Holidays;
import com.helger.holiday.parser.ChristianHolidayParser;
import com.helger.holiday.parser.EthiopianOrthodoxHolidayParser;
import com.helger.holiday.parser.FixedParser;
import com.helger.holiday.parser.FixedWeekdayBetweenFixedParser;
import com.helger.holiday.parser.FixedWeekdayInMonthParser;
import com.helger.holiday.parser.FixedWeekdayRelativeToFixedParser;
import com.helger.holiday.parser.HinduHolidayParser;
import com.helger.holiday.parser.IHolidayParser;
import com.helger.holiday.parser.IslamicHolidayParser;
import com.helger.holiday.parser.RelativeToFixedParser;
import com.helger.holiday.parser.RelativeToWeekdayInMonthParser;
import com.helger.jaxb.JAXBContextCache;

/**
 * Manager implementation for reading data from XML files. The files with the
 * name pattern Holidays_[country].xml will be read from the system classpath.
 * It uses a list a parsers for parsing the different type of XML nodes.
 *
 * @author Sven Diedrichsen
 * @author Philip Helger
 */
public class XMLHolidayManager extends AbstractHolidayManager
{
  private static final Logger s_aLogger = LoggerFactory.getLogger (XMLHolidayManager.class);

  /**
   * Unmarshals the configuration from the stream. Uses JAXB for
   * this.
   *
   * @param aIS
   * @return The unmarshalled configuration.
   */
  @Nonnull
  private static Configuration _unmarshallConfiguration (@WillClose @Nonnull final InputStream aIS)
  {
    ValueEnforcer.notNull (aIS, "InputStream");

    try
    {
      final JAXBContext ctx = JAXBContextCache.getInstance ()
                                              .getFromCache (com.helger.holiday.jaxb.ObjectFactory.class);
      final Unmarshaller um = ctx.createUnmarshaller ();
      final JAXBElement  aElement = GenericReflection.uncheckedCast (um.unmarshal (aIS));
      return aElement.getValue ();
    }
    catch (final JAXBException ex)
    {
      throw new IllegalArgumentException ("Cannot parse holidays XML.", ex);
    }
    finally
    {
      StreamHelper.close (aIS);
    }
  }

  /**
   * Configuration parsed on initialization.
   */
  private final Configuration m_aConfiguration;

  public XMLHolidayManager (@Nonnull @Nonempty final String sCountryCode)
  {
    ValueEnforcer.notEmpty (sCountryCode, "CountryCode");

    final String sFileName = "holidays/Holidays_" + sCountryCode.toLowerCase (CGlobal.DEFAULT_LOCALE) + ".xml";
    final InputStream aIS = ClassPathResource.getInputStream (sFileName);
    if (aIS == null)
      throw new IllegalArgumentException ("No holidays are defined for country code '" + sCountryCode + "'");
    m_aConfiguration = _unmarshallConfiguration (aIS);
    _validateConfigurationHierarchy (m_aConfiguration);
  }

  /**
   * Calls
   * Set<LocalDate> getHolidays(int year, Configuration c, String... args)
   * with the configuration from initialization.
   */
  public HolidayMap getHolidays (final int nYear, @Nullable final String... aArgs)
  {
    return _getHolidays (nYear, m_aConfiguration, aArgs);
  }

  /**
   * Creates a list of parsers by reading the configuration and trying to find
   * an HolidayParser implementation for by XML class type.
   *
   * @param aConfig
   * @return A list of parsers to for this configuration.
   */
  @Nonnull
  @ReturnsMutableCopy
  private static ICommonsList  _getParsers (@Nonnull final Holidays aConfig)
  {
    final ICommonsList  ret = new CommonsArrayList <> ();
    if (!aConfig.getChristianHoliday ().isEmpty ())
      ret.add (ChristianHolidayParser.getInstance ());
    if (!aConfig.getEthiopianOrthodoxHoliday ().isEmpty ())
      ret.add (EthiopianOrthodoxHolidayParser.getInstance ());
    if (!aConfig.getFixed ().isEmpty ())
      ret.add (FixedParser.getInstance ());
    if (!aConfig.getFixedWeekdayBetweenFixed ().isEmpty ())
      ret.add (FixedWeekdayBetweenFixedParser.getInstance ());
    if (!aConfig.getFixedWeekday ().isEmpty ())
      ret.add (FixedWeekdayInMonthParser.getInstance ());
    if (!aConfig.getFixedWeekdayRelativeToFixed ().isEmpty ())
      ret.add (FixedWeekdayRelativeToFixedParser.getInstance ());
    if (!aConfig.getHinduHoliday ().isEmpty ())
      ret.add (HinduHolidayParser.getInstance ());
    if (!aConfig.getIslamicHoliday ().isEmpty ())
      ret.add (IslamicHolidayParser.getInstance ());
    if (!aConfig.getRelativeToFixed ().isEmpty ())
      ret.add (RelativeToFixedParser.getInstance ());
    if (!aConfig.getRelativeToWeekdayInMonth ().isEmpty ())
      ret.add (RelativeToWeekdayInMonthParser.getInstance ());
    return ret;
  }

  /**
   * Parses the provided configuration for the provided year and fills the list
   * of holidays.
   *
   * @param nYear
   * @param aConfig
   * @param aArgs
   * @return the holidays
   */
  @Nonnull
  @ReturnsMutableCopy
  private HolidayMap _getHolidays (final int nYear,
                                   @Nonnull final Configuration aConfig,
                                   @Nullable final String... aArgs)
  {
    if (s_aLogger.isDebugEnabled ())
      s_aLogger.debug ("Adding holidays for " + aConfig.getDescription ());

    final HolidayMap aHolidayMap = new HolidayMap ();
    for (final IHolidayParser aParser : _getParsers (aConfig.getHolidays ()))
      aParser.parse (nYear, aHolidayMap, aConfig.getHolidays ());

    if (ArrayHelper.isNotEmpty (aArgs))
    {
      final String sHierarchy = aArgs[0];
      for (final Configuration aSubConfig : aConfig.getSubConfigurations ())
      {
        if (sHierarchy.equalsIgnoreCase (aSubConfig.getHierarchy ()))
        {
          // Recursive call
          final HolidayMap aSubHolidays = _getHolidays (nYear,
                                                        aSubConfig,
                                                        ArrayHelper.getCopy (aArgs, 1, aArgs.length - 1));
          aHolidayMap.addAll (aSubHolidays);
          break;
        }
      }
    }
    return aHolidayMap;
  }

  /**
   * Validates the content of the provided configuration by checking for
   * multiple hierarchy entries within one configuration. It traverses down the
   * configuration tree.
   */
  private static void _validateConfigurationHierarchy (@Nonnull final Configuration aConfig)
  {
    final ICommonsSet  aHierarchySet = new CommonsHashSet <> ();
    for (final Configuration aSubConfig : aConfig.getSubConfigurations ())
    {
      final String sHierarchy = aSubConfig.getHierarchy ();
      if (!aHierarchySet.add (sHierarchy))
        throw new IllegalArgumentException ("Configuration for " +
                                            aConfig.getHierarchy () +
                                            " contains multiple SubConfigurations with the same hierarchy id '" +
                                            sHierarchy +
                                            "'. ");
    }

    for (final Configuration aSubConfig : aConfig.getSubConfigurations ())
      _validateConfigurationHierarchy (aSubConfig);
  }

  /**
   * Returns the configurations hierarchy.
* i.e. Hierarchy 'us' -> Children 'al','ak','ar', ... ,'wv','wy'. Every * child might itself have children. The ids be used to call * getHolidays()/isHoliday(). */ @Override @Nonnull public CalendarHierarchy getHierarchy () { return _createConfigurationHierarchy (m_aConfiguration, null); } /** * Creates the configuration hierarchy for the provided configuration. * * @param aConfig * @return configuration hierarchy */ @Nonnull private static CalendarHierarchy _createConfigurationHierarchy (@Nonnull final Configuration aConfig, @Nullable final CalendarHierarchy aParent) { final ECountry eCountry = ECountry.getFromIDOrNull (aConfig.getHierarchy ()); final CalendarHierarchy aHierarchy = new CalendarHierarchy (aParent, aConfig.getHierarchy (), eCountry); for (final Configuration aSubConfig : aConfig.getSubConfigurations ()) { final CalendarHierarchy aSubHierarchy = _createConfigurationHierarchy (aSubConfig, aHierarchy); aHierarchy.addChild (aSubHierarchy); } return aHierarchy; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy