PoemLocale.java

/*
 * $Source$
 * $Revision$
 *
 * Copyright (C) 2000 William Chesters
 *
 * Part of Melati (http://melati.org), a framework for the rapid
 * development of clean, maintainable web applications.
 *
 * Melati is free software; Permission is granted to copy, distribute
 * and/or modify this software under the terms either:
 *
 * a) the GNU General Public License as published by the Free Software
 *    Foundation; either version 2 of the License, or (at your option)
 *    any later version,
 *
 *    or
 *
 * b) any version of the Melati Software License, as published
 *    at http://melati.org
 *
 * You should have received a copy of the GNU General Public License and
 * the Melati Software License along with this program;
 * if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
 * GNU General Public License and visit http://melati.org to obtain the
 * Melati Software License.
 *
 * Feel free to contact the Developers of Melati (http://melati.org),
 * if you would like to work out a different arrangement than the options
 * outlined here.  It is our intention to allow Melati to be used by as
 * wide an audience as possible.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * Contact details for copyright holder:
 *
 *     William Chesters <williamc At paneris.org>
 *     http://paneris.org/~williamc
 *     Obrechtstraat 114, 2517VX Den Haag, The Netherlands
 */

package org.melati.poem;

import java.text.SimpleDateFormat;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Locale;
import java.text.DateFormat;
import java.text.DateFormatSymbols;

import org.melati.poem.util.StringUtils;

/**
 * A wrapper for a <code>Locale</code> for use within Melati.
 */
public class PoemLocale {

  private static final HashMap<Locale, PoemLocale> localeCache = new HashMap<Locale, PoemLocale>();

  /** Default Locale: GB. */
  public static final PoemLocale HERE = new PoemLocale(Locale.UK);

  private final Locale locale;
  
  private final DateFormatSymbols dateFormatSymbols;
  private final String[] months, shortMonths;
  private final DateFormat[] dateFormats;
  private final DateFormat[] timestampFormats;
  
  /**
   * Creates a melati locale from a language tag as defined in RFC3066.
   * 
   * @param tag A language tag, for example, "en-gb"
   * @return A melati locale from the tag if we can parse it, otherwise null
   */
  public static PoemLocale fromLanguageTag(String tag) {
    String subtags[] = StringUtils.split(tag, '-');

    // if 1st subtag is 2 letters, then it's a 2 letter language code
    if (subtags.length > 0 && subtags[0].length() == 2) {
      Locale locale = null;
      // if 2nd subtag exists and is 2 letters, then it's a 2 letter county code
      if (subtags.length > 1 && subtags[1].length() == 2)
        locale = new Locale(subtags[0], subtags[1]);
      else
        locale = new Locale(subtags[0], "");
      return new PoemLocale(locale);
    }
    return null;
  } 
  
  public static PoemLocale from(Locale locale) {
    if (locale == null)
      throw new NullPointerException();
    PoemLocale it = localeCache.get(locale);
    if(it == null)
      localeCache.put(locale, new PoemLocale(locale));
    return localeCache.get(locale);
  }

  /**
   * Constructor given a non-null Locale. 
   * 
   * @param locale The Locale to base ours on.
   */
  public PoemLocale(Locale locale) {
    if (locale == null)
      throw new NullPointerException();
    this.locale = locale;

    dateFormatSymbols = new DateFormatSymbols(locale);
    months = dateFormatSymbols.getMonths();
    shortMonths = dateFormatSymbols.getShortMonths();

    dateFormats = new DateFormat[4]; // don't tell me this will break
    dateFormats[DateFormat.FULL] =
        DateFormat.getDateInstance(DateFormat.FULL, locale);
    dateFormats[DateFormat.LONG] =
        DateFormat.getDateInstance(DateFormat.LONG, locale);
    // Not happy about the change, even if it is an 'improvement' in java8
    dateFormats[DateFormat.MEDIUM] = locale == Locale.UK ? new SimpleDateFormat("DD-MMM-YYYY") :
        DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
    dateFormats[DateFormat.SHORT] =
        DateFormat.getDateInstance(DateFormat.SHORT, locale);

    timestampFormats = new DateFormat[4]; // don't tell me this will break
    timestampFormats[DateFormat.FULL] =
        DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL,
                                       locale);
    timestampFormats[DateFormat.LONG] =
        DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG,
                                       locale);
    timestampFormats[DateFormat.MEDIUM] =
        DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM,
                                       locale);
    timestampFormats[DateFormat.SHORT] =
        DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT,
                                       locale);
  }

  /**
   * @return the Locale
   */
  public final Locale locale() {
    return locale;
  }

  /**
   * @param monthNum numeric month
   * @return full name of month
   */
  public String monthName(int monthNum) {
    return months[monthNum - 1];
  }

  /**
   * @param monthNum numeric month
   * @return short name of month
   */
  public String shortMonthName(int monthNum) {
    return shortMonths[monthNum - 1];
  }

  /**
   * @param style as defined in DateFormat
   * @return a format of that style
   */
  public DateFormat dateFormat(int style) {
    return dateFormats[style];
  }

  /**
   * @param style as defined in DateFormat
   * @return a format of that style
   */
  public DateFormat timestampFormat(int style) {
    return timestampFormats[style];
  }

  /**
   * Delegated to Locale.
   * 
   * @see java.util.Locale#hashCode()
   * {@inheritDoc}
   * @see java.lang.Object#hashCode()
   */
  public int hashCode() {
    return locale.hashCode();
  }

  /**
   * {@inheritDoc}
   * @see java.lang.Object#equals(java.lang.Object)
   */
  public boolean equals(Object o) {
    if (this == o)     // quick check
      return true;
    if (o instanceof PoemLocale) 
      return locale.equals(((PoemLocale)o).locale());
    else 
      return false;
  }
  
  /**
   * Delegated to Locale.
   * 
   * {@inheritDoc}
   * @see java.lang.Object#toString()
   */
  public String toString() {
    return locale.toString();
  }
}