// ============================================================================
// File:               I18n.java
//
// Project:            I18n support.
//
// Purpose:            Application specific support for internationalisation.
//
// Author:             Rammi
//-----------------------------------------------------------------------------
// Copyright Notice:   (c) 2002  Rammi (rammi@caff.de)
//                     This code is in the public domain.
//                     Use at own risk.
//                     No guarantees given.
//
// Latest change:      $Date$
//
// History:	       $Log$
//=============================================================================

package de.caff.i18n;

import de.caff.util.Utility;

import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

/**
 *  Application specific support for internationalisation.
 *  This allows support for several resource classes for
 *  one application but <b>has the big disadvantage that
 *  separate resource bundles may  not define the same
 *  keys</b>.  Beware of that! You can switch DEBUG on
 *  to see if this happens.<p>
 *  This design should be overworked when possible.
 *  <p>
 *  To allow for enhancements the previous static design
 *  is now switched to something more object-oriented.
 *  This allows to take advantages of the new possibilities
 *  introduced with Java 1.2.
 *
 *  @author <a href="mailto:rammi@caff.de">Rammi</a>
 *  @version $Revision$
 */
public abstract class I18n
{
  /** Switch on to see whether there are key clashes. */
  static boolean     DEBUG            = false;

  /** 
   *  A singleton of the real I18n class which manages the
   *  I18n handling.
   */
  private static final I18n i18n;
  /** The i18n tag suffix added for a label/button text/menu text. */
  public static final String SUFFIX_TEXT        = "-TEXT";
  /** The i18n tag suffix added for a tooltip. */
  public static final String SUFFIX_TOOLTIP     = "-TTT";
  /** The i18n tag suffix added for a description. */
  public static final String SUFFIX_DESCRIPTION = "-DESCR";
  /** The i18n tag suffix added for an accelarator. */
  public static final String SUFFIX_ACCELERATOR = "-ACCEL";
  /** The i18n tag suffix added for a mnemonic. */
  public static final String SUFFIX_MNEMONIC    = "-MNEMO";
  /** The i18n tag suffix added for an icon path. */
  public static final String SUFFIX_ICON        = "-ICON";
  /** The i18n tag suffix added for the path of an icon used for the disabled state. */
  public static final String SUFFIX_DISABLED_ICON  = "-ICON-DIS";
  /** The i18n tag suffix added for the path of an icon used for the inactive state. */
  public static final String SUFFIX_INACTIVE_ICON  = "-ICON-INACT";

  /**
   *  Static initializer for i18n.
   *  A singelton will be constructed depending on the environment
   *  we run in.
   */
  static {
    String vmversion;
    try {
      vmversion = System.getProperty("java.vm.version");
    } catch (Throwable x) {
      vmversion = null;
    }
    I18n tmp = new I18n4Java1();

    if (vmversion != null  &&  !vmversion.startsWith("1.1")) {
      try {
	tmp = (I18n)Class.forName("de.caff.i18n.java2.I18n4Java2").newInstance();
      } catch (Throwable x) {
	// fallthrough
      }
    }
    // set the final var
    i18n = tmp;
  }

  /**
   *  Add an application specific resource class base name.
   *  This should be called by any application/applet before
   *  any other i18n specific code is used.
   *  @param  base  base class name for resources
   *  @see java.util.ResourceBundle
   */
  public static void addAppResourceBase(String base)
  {
    if (i18n != null) {
      i18n._addAppResourceBase(base);
    }
  }

  /**
   *  Add an application specific resource class base name.
   *  This should be called by any application/applet before
   *  any other i18n specific code is used.
   *  @param  base  base class name for resources
   *  @see java.util.ResourceBundle
   */
  protected abstract void _addAppResourceBase(String base);

  /**
   *  Set the locale to be used as a default for the application.
   *  @param  l   locale to be used as default
   */
  public static void setDefaultLocale(Locale l)
  {
    i18n._setDefaultLocale(l);
  }


  /**
   *  Set the locale to be used as a default for the application.
   *  @param  l   locale to be used as default
   */
  protected abstract void _setDefaultLocale(Locale l);


  /**
   *  Get the locale to be used as a default for the application.
   *  @return   locale to be used as default
   */
  public static Locale getDefaultLocale()
  {
    return i18n._getDefaultLocale();
  }

  /**
   *  Get the locale to be used as a default for the application.
   *  @return   locale to be used as default
   */
  protected abstract Locale _getDefaultLocale(); 

  /**
   *  Get a ResourceBundle for a locale.
   *  @param  l  locale
   *  @return ResourceBundle for that Locale
   *  @exception MissingResourceException  when no appResourceBase is set
   */
  private static final ResourceBundle getBundle(Locale l)
    throws MissingResourceException 
  {
    return i18n._getBundle(l);
  }

  /**
   *  Get a ResourceBundle for a locale.
   *  @param  l  locale
   *  @return ResourceBundle for that Locale
   *  @exception MissingResourceException  when no appResourceBase is set
   */
  protected abstract ResourceBundle _getBundle(Locale l)
    throws MissingResourceException;


  /**
   *  Get a string for the default locale.
   *  @param   tag     resource tag
   *  @return  localized string
   *  @throws  MissingResourceException    when no appResourceBase is set
   */
  public static String getString(String tag)
          throws MissingResourceException
  {
    return getString(tag, null);
  }


  /**
   *  Get a String specified by a Locale.
   *  @param   tag     resource tag
   *  @param   l       Locale to be used
   *  @return  localized String
   *  @exception MissingResourceException  when no appResourceBase is set
   *  @see java.util.ResourceBundle#getString
   */
  public static String getString(String tag, Locale l)
          throws MissingResourceException
  {
    // System.out.println("Tag = "+tag+", l = "+(l != null ? l.getDisplayName() :  "<unknown>")+", returning \'"+ getBundle(l).getString(tag)+"'");
    return getBundle(l).getString(tag);
  }

  /**
   *  Compile a String with a format using the default locale.
   *  @see de.caff.util.Utility#compileString
   *  @param  tag   resource tag of format
   *  @param  args  arguments
   *  @return formatted string
   *  @exception MissingResourceException  when no appResourceBase is set
   */
  public static String format(String tag, Object[] args)
    throws MissingResourceException 
  {
    return format(tag, args, null);
  }

  /**
   *  Compile a String with a format using the default locale.
   *
   *  This is a convenience method for using {@link #format(String, Object[])} with just one argument.
   *  @see de.caff.util.Utility#compileString
   *  @param  tag   resource tag of format
   *  @param  arg   argument
   *  @return formatted string
   *  @exception MissingResourceException  when no appResourceBase is set
   */
  public static String format(String tag, Object arg)
          throws MissingResourceException
  {
    return format(tag, new Object[] { arg });
  }

  /**
   *  Compile a String with a format using the default locale.
   *
   *  This is a convenience method for using {@link #format(String, Object[])} with two arguments.
   *  @see de.caff.util.Utility#compileString
   *  @param  tag   resource tag of format
   *  @param  arg1  first argument
   *  @param  arg2  second argument
   *  @return formatted string
   *  @exception MissingResourceException  when no appResourceBase is set
   */
  public static String format(String tag, Object arg1, Object arg2)
          throws MissingResourceException
  {
    return format(tag, new Object[] { arg1, arg2 });
  }

  /**
   *  Compile a String with a format using the default locale.
   *
   *  This is a convenience method for using {@link #format(String, Object[])} with three arguments.
   *  @see de.caff.util.Utility#compileString
   *  @param  tag   resource tag of format
   *  @param  arg1  first argument
   *  @param  arg2  second argument
   *  @param  arg3  second argument
   *  @return formatted string
   *  @exception MissingResourceException  when no appResourceBase is set
   */
  public static String format(String tag, Object arg1, Object arg2, Object arg3)
          throws MissingResourceException
  {
    return format(tag, new Object[] { arg1, arg2, arg3 });
  }

  /**
   *  Compile a String with a format.
   *  @see de.caff.util.Utility#compileString
   *  @param  tag   resource tag of format
   *  @param  args  arguments
   *  @param  l     locale
   *  @return formatted string
   *  @exception MissingResourceException  when no appResourceBase is set
   */
  public static String format(String tag, Object[] args, Locale l)
    throws MissingResourceException
  {
    return Utility.compileString(tag, args, getBundle(l));
  }

  /**
   *  Compile a String with a format.
   *
   *  This is a convenience method for using {@link #format(String, Object[], Locale)} with just one argument.
   *  @see de.caff.util.Utility#compileString
   *  @param  tag   resource tag of format
   *  @param  arg   argument
   *  @param  l     locale
   *  @return formatted string
   *  @exception MissingResourceException  when no appResourceBase is set
   */
  public static String format(String tag, Object arg, Locale l)
          throws MissingResourceException
  {
    return format(tag, new Object[] { arg }, l);
  }

  /**
   *  Compile a String with a format.
   *
   *  This is a convenience method for using {@link #format(String, Object[], Locale)} with two arguments.
   *  @see de.caff.util.Utility#compileString
   *  @param  tag   resource tag of format
   *  @param  arg1  argument
   *  @param  arg2  argument
   *  @param  l     locale
   *  @return formatted string
   *  @exception MissingResourceException  when no appResourceBase is set
   */
  public static String format(String tag, Object arg1, Object arg2, Locale l)
          throws MissingResourceException
  {
    return format(tag, new Object[] { arg1, arg2 }, l);
  }

  /**
   *  Compile a String with a format.
   *
   *  This is a convenience method for using {@link #format(String, Object[], Locale)} with two arguments.
   *  @see de.caff.util.Utility#compileString
   *  @param  tag   resource tag of format
   *  @param  arg1  argument
   *  @param  arg2  argument
   *  @param  arg3  argument
   *  @param  l     locale
   *  @return formatted string
   *  @exception MissingResourceException  when no appResourceBase is set
   */
  public static String format(String tag, Object arg1, Object arg2, Object arg3, Locale l)
          throws MissingResourceException
  {
    return format(tag, new Object[] { arg1, arg2, arg3 }, l);  
  }

  /**
   *  Add a listener for localization changes.
   *  @param  localizable  listener for changes
   */
  public static void addLocalizationChangeListener(Localizable localizable)
  {
    i18n._addLocalizationChangeListener(localizable);
  }

  /**
   *  Add a listener for localization changes.
   *  @param  localizable  listener for changes
   */
  protected abstract void _addLocalizationChangeListener(Localizable localizable);

  /**
   *  Remove a listener for localization changes.
   *  @param  localizable  listener to be removed
   */
  public static void removeLocalizationChangeListener(Localizable localizable)
  {
    i18n._removeLocalizationChangeListener(localizable);
  }

  /**
   *  Remove a listener for localization changes.
   *  @param  localizable  listener to be removed
   */
  protected abstract void _removeLocalizationChangeListener(Localizable localizable);

  /**
   *  Tell all registered localizables of localization changes.
   *  @param  locale  new locale
   */
  protected abstract void _fireLocaleChanged(Locale locale);
}

