java - 未加载应用程序特定语言环境变体的 DateFormatProvider 实现

标签 java

我有一个 DateFormatProvider 的实现打包在一个带有适当的 java.text.spi.DateFormatProvider 文件的 jar 中,但是 DateFormatProvider 将不会被使用(事实上 LocaleServiceProviderPool.getPool(DateFormatProvider.class).hasEntries() 在下面的测试程序中为false。

DateFormatProvider 的实现:

package dateformatproviders;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.text.spi.DateFormatProvider;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Locale;

public class DateFormatProviderImpl extends DateFormatProvider
{
  private static final CompareLocales COMPARE_LOCALES = new CompareLocales();
  private Locale[] availableLocales;
  private static final String VARIANT = "variant_xxx";
  private final String formatString;
  private final Object availableLocalesLock = new Object();

  /**
   * for each available locale create a variant using the given variantString.
   * Return the list of all these locales
   *
   * @param variantString must not be null
   * @return never null nor empty
   */
  public static Locale[] installVariant(String variantString)
  {
    Locale[] availableLocales = Locale.getAvailableLocales();
    Locale[] result = new Locale[availableLocales.length];
    int pos = 0;
    for (Locale locale : availableLocales)
    {
      Locale newLocale = createLocale(locale, variantString);
      result[pos] = newLocale;
      ++pos;
    }
    return result;
  }

  public static Locale createLocale(Locale originalDefaultLocale, String variant)
  {
    Locale modifiedDefaultLocale = new Locale(originalDefaultLocale.getLanguage(), originalDefaultLocale.getCountry(), variant);
    return modifiedDefaultLocale;
  }

  public DateFormatProviderImpl()
  {
    formatString = "HH:mm:ss 't' MM/dd/yy";
  }

  private DateFormat createDateFormat()
  {
    return new SimpleDateFormat(formatString);
  }

  @Override
  public final DateFormat getTimeInstance(int style, Locale locale)
  {
    checkArguments(locale, style);
    return createDateFormat();
  }

  @Override
  public final DateFormat getDateInstance(int style, Locale locale)
  {
    checkArguments(locale, style);
    return createDateFormat();
  }

  @Override
  public final DateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale locale)
  {
    checkArguments(locale, dateStyle, timeStyle);
    return createDateFormat();
  }

  @Override
  public final Locale[] getAvailableLocales()
  {
    synchronized (availableLocalesLock)
    {
      if (null == availableLocales)
      {
        Locale[] sourceLocales = installVariant(VARIANT);
        availableLocales = Arrays.copyOf(sourceLocales, sourceLocales.length);
        Arrays.sort(availableLocales, COMPARE_LOCALES);
      }
    }
    return availableLocales;
  }

  public final String getVariant()
  {
    return VARIANT;
  }

  private void checkArguments(Locale locale, int... styles)
  {
    for (int style : styles)
    {
      switch (style)
      {
        case DateFormat.SHORT:
        case DateFormat.MEDIUM:

        case DateFormat.LONG:
        case DateFormat.FULL:
          break;
        default:
          throw new IllegalArgumentException("style:" + style + " must be one of{" + DateFormat.SHORT + ',' + DateFormat.MEDIUM + ','
                  + DateFormat.LONG + ',' + DateFormat.FULL + '}');
      }
    }
    checkLocale(locale);
  }

  private void checkLocale(Locale locale)
  {
    if (null == locale)
    {
      throw new IllegalArgumentException("locale must not be null");
    }
    if (Arrays.binarySearch(availableLocales, locale, COMPARE_LOCALES) < 0)
    {
      throw new IllegalArgumentException("locale not supported:" + locale);
    }
  }

  private static class CompareLocales implements Comparator<Locale>
  {
    @Override
    public int compare(Locale lhs, Locale rhs)
    {
      if (null == lhs)
      {
        if (null == rhs)
        {
          return 0;
        }
        return -1;
      }
      if (null == rhs)
      {
        return 1;
      }
      String lhsLang = lhs.getLanguage();
      String rhsLang = rhs.getLanguage();
      int langCompare = lhsLang.compareTo(rhsLang);
      if (0 == langCompare)
      {
        String lhsCountry = lhs.getCountry();
        String rhsCountry = rhs.getCountry();
        int countryCompare = lhsCountry.compareTo(rhsCountry);
        if (0 == countryCompare)
        {
          String lhsVariant = lhs.getVariant();
          String rhsVariant = rhs.getVariant();
          return lhsVariant.compareTo(rhsVariant);
        }
        return countryCompare;
      }
      return langCompare;
    }
  }
}

META-INF/services/java.text.spi.DateFormatProvider:

dateformatproviders.DateFormatProviderImpl

测试程序:

package datetest;

import dateformatproviders.DateFormatProviderImpl;
import java.text.DateFormat;
import java.text.spi.DateFormatProvider;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import sun.util.LocaleServiceProviderPool;

public class Main
{
  /**
   * install the given dateFormatProvider for the default locale, using the default variant. <br/>
   * Changes the default locale
   *
   * @param dateFormatProvider
   */
  public static void install(DateFormatProviderImpl dateFormatProvider)
  {
    Locale originalDefaultLocale = Locale.getDefault();
    Locale modifiedDefaultLocale = DateFormatProviderImpl.createLocale(originalDefaultLocale, dateFormatProvider.getVariant());
    Locale.setDefault(modifiedDefaultLocale);
  }

  public static void main(String[] args) throws InterruptedException
  {
    install(new DateFormatProviderImpl());
    LocaleServiceProviderPool pool = LocaleServiceProviderPool.getPool(DateFormatProvider.class);
    boolean hasEntries = pool.hasProviders(); // is false
    while (true)
    {
      System.out.printf("%s\n", DateFormat.getDateTimeInstance().format(new Date()));
      TimeUnit.SECONDS.sleep(2);
    }

  }
}

在包含 DateTest.jar 的目录和包含 DateFormatProvider.jar 的文件夹 lib 中执行:

java -cp DateTest.jar:lib/DateFormatProvider.jar dateTest.Main

生成默认格式的日期

当包含 DateFormatProvider 及其 java.text.spi.DateFormatProvider 文件的 jar 文件被复制到 jre/lib/ext 时,测试程序仍然有效。

最佳答案

根据official documentation关于可选包(这是 JVM 扩展的新名称),有两种方法可以将 JAR 文件用作可选包:

  • 通过放置在 Java 2 运行时环境或 JDK 目录结构中的特殊位置 - 在这种情况下它是 installed optional package .

  • 以特定方式从小应用程序或应用程序的 JAR 文件的 list 中引用 - 在这种情况下它是 download optional package .

仅将 JAR 放在类路径中不足以将其作为可选包加载。但是当您将 JAR 文件放在 jre/lib/ext 目录中时,您使用的是第一种方式。

文档还包含这条注释:

Another difference between installed and download optional packages is that only applets and applications bundled in a JAR file can make use of download optional packages. Applets and applications not bundled in a JAR file don't have a manifest from which to reference download optional packages.

另请注意,已弃用已安装的可选包:

Deprecated: Support for installed optional packages has been deprecated and may be removed in a future release.

关于java - 未加载应用程序特定语言环境变体的 DateFormatProvider 实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16149945/

相关文章:

java - 未指定尺寸的 Android 裁剪图像

java - jasperreport呈现空pdf

java - 如何从最小窗口大小更改 JFrame 大小?

java - 如何使用实体监听器和回调方法访问 oldObject 和 newObject

java - : Expected condition failed: waiting for visibility of element located by(. 的 Selenium webdriver 问题。)

java - Oracle SqlDeveloper 找不到正确的 JDK

java - 我的应用程序内存泄漏?代码中的垃圾收集是一种好的做法吗?

java - 在 Intellij 中,如何创建一个为测试添加导入语句的实时模板?

java - 如何在 Firebase 中按字段过滤项目?

Java - 定义和访问注释?