Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  OSPreferences.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


/**
 * This is a shared part of the OSPreferences API implementation.
 * It defines helper methods and public methods that are calling
 * platform-specific private methods.
 */


#include "OSPreferences.h"

#include "mozilla/ClearOnShutdown.h"
#include "mozilla/intl/DateTimePatternGenerator.h"
#include "mozilla/intl/DateTimeFormat.h"
#include "mozilla/intl/LocaleService.h"
#include "mozilla/Preferences.h"
#include "mozilla/Result.h"
#include "mozilla/Services.h"
#include "nsIObserverService.h"

using namespace mozilla::intl;

NS_IMPL_ISUPPORTS(OSPreferences, mozIOSPreferences)

mozilla::StaticRefPtr<OSPreferences> OSPreferences::sInstance;

// Return a new strong reference to the instance, creating it if necessary.
already_AddRefed<OSPreferences> OSPreferences::GetInstanceAddRefed() {
  RefPtr<OSPreferences> result = sInstance;
  if (!result) {
    MOZ_ASSERT(NS_IsMainThread(),
               "OSPreferences should be initialized on main thread!");
    if (!NS_IsMainThread()) {
      return nullptr;
    }
    sInstance = new OSPreferences();
    result = sInstance;

    DebugOnly<nsresult> rv = Preferences::RegisterPrefixCallback(
        PreferenceChanged, "intl.date_time.pattern_override");
    MOZ_ASSERT(NS_SUCCEEDED(rv), "Adding observers failed.");

    ClearOnShutdown(&sInstance);
  }
  return result.forget();
}

// Return a raw pointer to the instance: not for off-main-thread use,
// because ClearOnShutdown means it could go away unexpectedly.
OSPreferences* OSPreferences::GetInstance() {
  MOZ_ASSERT(NS_IsMainThread());
  if (!sInstance) {
    // This will create the static instance; then we just drop the extra
    // reference.
    RefPtr<OSPreferences> result = GetInstanceAddRefed();
  }
  return sInstance;
}

void OSPreferences::Refresh() {
  nsTArray<nsCString> newLocales;
  ReadSystemLocales(newLocales);

  if (mSystemLocales != newLocales) {
    mSystemLocales = std::move(newLocales);
    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    if (obs) {
      obs->NotifyObservers(nullptr, "intl:system-locales-changed", nullptr);
    }
  }
}

OSPreferences::~OSPreferences() {
  Preferences::UnregisterPrefixCallback(PreferenceChanged,
                                        "intl.date_time.pattern_override");
  RemoveObservers();
}

/*static*/
void OSPreferences::PreferenceChanged(const char* aPrefName,
                                      void/* aClosure */) {
  if (sInstance) {
    sInstance->mPatternCache.Clear();
  }
}

/**
 * This method should be called by every method of OSPreferences that
 * retrieves a locale id from external source.
 *
 * It attempts to retrieve as much of the locale ID as possible, cutting
 * out bits that are not understood (non-strict behavior of ICU).
 *
 * It returns true if the canonicalization was successful.
 */

bool OSPreferences::CanonicalizeLanguageTag(nsCString& aLoc) {
  return LocaleService::CanonicalizeLanguageId(aLoc);
}

/**
 * This method retrieves from mozilla::intl the best pattern for a given
 * date/time style.
 */

bool OSPreferences::GetDateTimePatternForStyle(DateTimeFormatStyle aDateStyle,
                                               DateTimeFormatStyle aTimeStyle,
                                               const nsACString& aLocale,
                                               nsACString& aRetVal) {
  DateTimeFormat::StyleBag style;

  switch (aTimeStyle) {
    case DateTimeFormatStyle::Short:
      style.time = Some(DateTimeFormat::Style::Short);
      break;
    case DateTimeFormatStyle::Medium:
      style.time = Some(DateTimeFormat::Style::Medium);
      break;
    case DateTimeFormatStyle::Long:
      style.time = Some(DateTimeFormat::Style::Long);
      break;
    case DateTimeFormatStyle::Full:
      style.time = Some(DateTimeFormat::Style::Full);
      break;
    case DateTimeFormatStyle::None:
    case DateTimeFormatStyle::Invalid:
      // Do nothing.
      break;
  }

  switch (aDateStyle) {
    case DateTimeFormatStyle::Short:
      style.date = Some(DateTimeFormat::Style::Short);
      break;
    case DateTimeFormatStyle::Medium:
      style.date = Some(DateTimeFormat::Style::Medium);
      break;
    case DateTimeFormatStyle::Long:
      style.date = Some(DateTimeFormat::Style::Long);
      break;
    case DateTimeFormatStyle::Full:
      style.date = Some(DateTimeFormat::Style::Full);
      break;
    case DateTimeFormatStyle::None:
    case DateTimeFormatStyle::Invalid:
      // Do nothing.
      break;
  }

  nsAutoCString locale;
  if (aLocale.IsEmpty()) {
    AutoTArray<nsCString, 10> regionalPrefsLocales;
    LocaleService::GetInstance()->GetRegionalPrefsLocales(regionalPrefsLocales);
    locale.Assign(regionalPrefsLocales[0]);
  } else {
    locale.Assign(aLocale);
  }

  auto genResult =
      DateTimePatternGenerator::TryCreate(PromiseFlatCString(aLocale).get());
  if (genResult.isErr()) {
    return false;
  }
  auto generator = genResult.unwrap();

  auto dfResult = DateTimeFormat::TryCreateFromStyle(
      MakeStringSpan(locale.get()), style, generator.get(), Nothing());
  if (dfResult.isErr()) {
    return false;
  }
  auto df = dfResult.unwrap();

  DateTimeFormat::PatternVector pattern;
  auto patternResult = df->GetPattern(pattern);
  if (patternResult.isErr()) {
    return false;
  }

  aRetVal = NS_ConvertUTF16toUTF8(pattern.begin(), pattern.length());
  return true;
}

/**
 * This method retrieves from mozilla::intl the best skeleton for a given
 * date/time style.
 *
 * This is useful for cases where an OS does not provide its own patterns,
 * but provide ability to customize the skeleton, like alter hourCycle setting.
 *
 * The returned value is a skeleton that matches the styles.
 */

bool OSPreferences::GetDateTimeSkeletonForStyle(DateTimeFormatStyle aDateStyle,
                                                DateTimeFormatStyle aTimeStyle,
                                                const nsACString& aLocale,
                                                nsACString& aRetVal) {
  nsAutoCString pattern;
  if (!GetDateTimePatternForStyle(aDateStyle, aTimeStyle, aLocale, pattern)) {
    return false;
  }

  auto genResult =
      DateTimePatternGenerator::TryCreate(PromiseFlatCString(aLocale).get());
  if (genResult.isErr()) {
    return false;
  }

  nsAutoString patternAsUtf16 = NS_ConvertUTF8toUTF16(pattern);
  DateTimeFormat::SkeletonVector skeleton;
  auto generator = genResult.unwrap();
  auto skeletonResult = generator->GetSkeleton(patternAsUtf16, skeleton);
  if (skeletonResult.isErr()) {
    return false;
  }

  aRetVal = NS_ConvertUTF16toUTF8(skeleton.begin(), skeleton.length());
  return true;
}

/**
 * This method checks for preferences that override the defaults
 */

bool OSPreferences::OverrideDateTimePattern(DateTimeFormatStyle aDateStyle,
                                            DateTimeFormatStyle aTimeStyle,
                                            const nsACString& aLocale,
                                            nsACString& aRetVal) {
  const auto PrefToMaybeString = [](const char* pref) -> Maybe<nsAutoCString> {
    nsAutoCString value;
    nsresult nr = Preferences::GetCString(pref, value);
    if (NS_FAILED(nr) || value.IsEmpty()) {
      return Nothing();
    }
    return Some(std::move(value));
  };

  Maybe<nsAutoCString> timeSkeleton;
  switch (aTimeStyle) {
    case DateTimeFormatStyle::Short:
      timeSkeleton =
          PrefToMaybeString("intl.date_time.pattern_override.time_short");
      break;
    case DateTimeFormatStyle::Medium:
      timeSkeleton =
          PrefToMaybeString("intl.date_time.pattern_override.time_medium");
      break;
    case DateTimeFormatStyle::Long:
      timeSkeleton =
          PrefToMaybeString("intl.date_time.pattern_override.time_long");
      break;
    case DateTimeFormatStyle::Full:
      timeSkeleton =
          PrefToMaybeString("intl.date_time.pattern_override.time_full");
      break;
    default:
      break;
  }

  Maybe<nsAutoCString> dateSkeleton;
  switch (aDateStyle) {
    case DateTimeFormatStyle::Short:
      dateSkeleton =
          PrefToMaybeString("intl.date_time.pattern_override.date_short");
      break;
    case DateTimeFormatStyle::Medium:
      dateSkeleton =
          PrefToMaybeString("intl.date_time.pattern_override.date_medium");
      break;
    case DateTimeFormatStyle::Long:
      dateSkeleton =
          PrefToMaybeString("intl.date_time.pattern_override.date_long");
      break;
    case DateTimeFormatStyle::Full:
      dateSkeleton =
          PrefToMaybeString("intl.date_time.pattern_override.date_full");
      break;
    default:
      break;
  }

  nsAutoCString locale;
  if (aLocale.IsEmpty()) {
    AutoTArray<nsCString, 10> regionalPrefsLocales;
    LocaleService::GetInstance()->GetRegionalPrefsLocales(regionalPrefsLocales);
    locale.Assign(regionalPrefsLocales[0]);
  } else {
    locale.Assign(aLocale);
  }

  const auto FillConnectorPattern = [&locale](
                                        const nsAutoCString& datePattern,
                                        const nsAutoCString& timePattern) {
    nsAutoCString pattern;
    GetDateTimeConnectorPattern(nsDependentCString(locale.get()), pattern);
    int32_t index = pattern.Find("{1}");
    if (index != kNotFound) {
      pattern.Replace(index, 3, datePattern);
    }
    index = pattern.Find("{0}");
    if (index != kNotFound) {
      pattern.Replace(index, 3, timePattern);
    }
    return pattern;
  };

  if (timeSkeleton && dateSkeleton) {
    aRetVal.Assign(FillConnectorPattern(*dateSkeleton, *timeSkeleton));
  } else if (timeSkeleton) {
    if (aDateStyle != DateTimeFormatStyle::None) {
      nsAutoCString pattern;
      if (!ReadDateTimePattern(aDateStyle, DateTimeFormatStyle::None, aLocale,
                               pattern) &&
          !GetDateTimePatternForStyle(aDateStyle, DateTimeFormatStyle::None,
                                      aLocale, pattern)) {
        return false;
      }
      aRetVal.Assign(FillConnectorPattern(pattern, *timeSkeleton));
    } else {
      aRetVal.Assign(*timeSkeleton);
    }
  } else if (dateSkeleton) {
    if (aTimeStyle != DateTimeFormatStyle::None) {
      nsAutoCString pattern;
      if (!ReadDateTimePattern(DateTimeFormatStyle::None, aTimeStyle, aLocale,
                               pattern) &&
          !GetDateTimePatternForStyle(DateTimeFormatStyle::None, aTimeStyle,
                                      aLocale, pattern)) {
        return false;
      }
      aRetVal.Assign(FillConnectorPattern(*dateSkeleton, pattern));
    } else {
      aRetVal.Assign(*dateSkeleton);
    }
  } else {
    return false;
  }

  return true;
}

/**
 * This function is a counterpart to GetDateTimeSkeletonForStyle.
 *
 * It takes a skeleton and returns the best available pattern for a given locale
 * that represents the provided skeleton.
 *
 * For example:
 * "Hm" skeleton for "en-US" will return "H:m"
 */

bool OSPreferences::GetPatternForSkeleton(const nsACString& aSkeleton,
                                          const nsACString& aLocale,
                                          nsACString& aRetVal) {
  aRetVal.Truncate();

  auto genResult =
      DateTimePatternGenerator::TryCreate(PromiseFlatCString(aLocale).get());
  if (genResult.isErr()) {
    return false;
  }

  nsAutoString skeletonAsUtf16 = NS_ConvertUTF8toUTF16(aSkeleton);
  DateTimeFormat::PatternVector pattern;
  auto generator = genResult.unwrap();
  auto patternResult = generator->GetBestPattern(skeletonAsUtf16, pattern);
  if (patternResult.isErr()) {
    return false;
  }

  aRetVal = NS_ConvertUTF16toUTF8(pattern.begin(), pattern.length());
  return true;
}

/**
 * This function returns a pattern that should be used to join date and time
 * patterns into a single date/time pattern string.
 *
 * It's useful for OSes that do not provide an API to retrieve such combined
 * pattern.
 *
 * An example output is "{1}, {0}".
 */

bool OSPreferences::GetDateTimeConnectorPattern(const nsACString& aLocale,
                                                nsACString& aRetVal) {
  // Check for a valid override pref and use that if present.
  nsAutoCString value;
  nsresult nr = Preferences::GetCString(
      "intl.date_time.pattern_override.connector_short", value);
  if (NS_SUCCEEDED(nr) && value.Find("{0}") != kNotFound &&
      value.Find("{1}") != kNotFound) {
    aRetVal = std::move(value);
    return true;
  }

  auto genResult =
      DateTimePatternGenerator::TryCreate(PromiseFlatCString(aLocale).get());
  if (genResult.isErr()) {
    return false;
  }

  auto generator = genResult.unwrap();
  Span<const char16_t> result = generator->GetPlaceholderPattern();
  aRetVal = NS_ConvertUTF16toUTF8(result.data(), result.size());
  return true;
}

/**
 * mozIOSPreferences methods
 */

NS_IMETHODIMP
OSPreferences::GetSystemLocales(nsTArray<nsCString>& aRetVal) {
  if (!mSystemLocales.IsEmpty()) {
    aRetVal = mSystemLocales.Clone();
    return NS_OK;
  }

  if (ReadSystemLocales(aRetVal)) {
    mSystemLocales = aRetVal.Clone();
    return NS_OK;
  }

  // If we failed to get the system locale, we still need
  // to return something because there are tests out there that
  // depend on system locale to be set.
  aRetVal.AppendElement("en-US"_ns);
  return NS_ERROR_FAILURE;
}

NS_IMETHODIMP
OSPreferences::GetSystemLocale(nsACString& aRetVal) {
  if (!mSystemLocales.IsEmpty()) {
    aRetVal = mSystemLocales[0];
  } else {
    AutoTArray<nsCString, 10> locales;
    GetSystemLocales(locales);
    if (!locales.IsEmpty()) {
      aRetVal = locales[0];
    }
  }
  return NS_OK;
}

NS_IMETHODIMP
OSPreferences::GetRegionalPrefsLocales(nsTArray<nsCString>& aRetVal) {
  if (!mRegionalPrefsLocales.IsEmpty()) {
    aRetVal = mRegionalPrefsLocales.Clone();
    return NS_OK;
  }

  if (ReadRegionalPrefsLocales(aRetVal)) {
    mRegionalPrefsLocales = aRetVal.Clone();
    return NS_OK;
  }

  // If we failed to read regional prefs locales,
  // use system locales as last fallback.
  return GetSystemLocales(aRetVal);
}

static OSPreferences::DateTimeFormatStyle ToDateTimeFormatStyle(
    int32_t aTimeFormat) {
  switch (aTimeFormat) {
    // See mozIOSPreferences.idl for the integer values here.
    case 0:
      return OSPreferences::DateTimeFormatStyle::None;
    case 1:
      return OSPreferences::DateTimeFormatStyle::Short;
    case 2:
      return OSPreferences::DateTimeFormatStyle::Medium;
    case 3:
      return OSPreferences::DateTimeFormatStyle::Long;
    case 4:
      return OSPreferences::DateTimeFormatStyle::Full;
  }
  return OSPreferences::DateTimeFormatStyle::Invalid;
}

NS_IMETHODIMP
OSPreferences::GetDateTimePattern(int32_t aDateFormatStyle,
                                  int32_t aTimeFormatStyle,
                                  const nsACString& aLocale,
                                  nsACString& aRetVal) {
  DateTimeFormatStyle dateStyle = ToDateTimeFormatStyle(aDateFormatStyle);
  if (dateStyle == DateTimeFormatStyle::Invalid) {
    return NS_ERROR_INVALID_ARG;
  }
  DateTimeFormatStyle timeStyle = ToDateTimeFormatStyle(aTimeFormatStyle);
  if (timeStyle == DateTimeFormatStyle::Invalid) {
    return NS_ERROR_INVALID_ARG;
  }

  // If the user is asking for None on both, date and time style,
  // let's exit early.
  if (timeStyle == DateTimeFormatStyle::None &&
      dateStyle == DateTimeFormatStyle::None) {
    return NS_OK;
  }

  // If the locale is not specified, default to first regional prefs locale
  const nsACString* locale = &aLocale;
  AutoTArray<nsCString, 10> rpLocales;
  if (aLocale.IsEmpty()) {
    LocaleService::GetInstance()->GetRegionalPrefsLocales(rpLocales);
    MOZ_ASSERT(rpLocales.Length() > 0);
    locale = &rpLocales[0];
  }

  // Create a cache key from the locale + style options
  nsAutoCString key(*locale);
  key.Append(':');
  key.AppendInt(aDateFormatStyle);
  key.Append(':');
  key.AppendInt(aTimeFormatStyle);

  nsCString pattern;
  if (mPatternCache.Get(key, &pattern)) {
    aRetVal = pattern;
    return NS_OK;
  }

  if (!OverrideDateTimePattern(dateStyle, timeStyle, *locale, pattern)) {
    if (!ReadDateTimePattern(dateStyle, timeStyle, *locale, pattern)) {
      if (!GetDateTimePatternForStyle(dateStyle, timeStyle, *locale, pattern)) {
        return NS_ERROR_FAILURE;
      }
    }
  }

  if (mPatternCache.Count() == kMaxCachedPatterns) {
    // Don't allow unlimited cache growth; just throw it away in the case of
    // pathological behavior where a page keeps requesting different formats
    // and locales.
    NS_WARNING("flushing DateTimePattern cache");
    mPatternCache.Clear();
  }
  mPatternCache.InsertOrUpdate(key, pattern);

  aRetVal = pattern;
  return NS_OK;
}

void OSPreferences::OverrideSkeletonHourCycle(bool aIs24Hour,
                                              nsAutoCString& aSkeleton) {
  if (aIs24Hour) {
    // If aSkeleton contains 'h' or 'K', replace with 'H' or 'k' respectively,
    // and delete 'a' if present.
    if (aSkeleton.FindChar('h') == -1 && aSkeleton.FindChar('K') == -1) {
      return;
    }
    for (int32_t i = 0; i < int32_t(aSkeleton.Length()); ++i) {
      switch (aSkeleton[i]) {
        case 'a':
          aSkeleton.Cut(i, 1);
          --i;
          break;
        case 'h':
          aSkeleton.SetCharAt('H', i);
          break;
        case 'K':
          aSkeleton.SetCharAt('k', i);
          break;
      }
    }
  } else {
    // If skeleton contains 'H' or 'k', replace with 'h' or 'K' respectively,
    // and add 'a' unless already present.
    if (aSkeleton.FindChar('H') == -1 && aSkeleton.FindChar('k') == -1) {
      return;
    }
    bool foundA = false;
    for (size_t i = 0; i < aSkeleton.Length(); ++i) {
      switch (aSkeleton[i]) {
        case 'a':
          foundA = true;
          break;
        case 'H':
          aSkeleton.SetCharAt('h', i);
          break;
        case 'k':
          aSkeleton.SetCharAt('K', i);
          break;
      }
    }
    if (!foundA) {
      aSkeleton.Append(char16_t('a'));
    }
  }
}

Messung V0.5
C=94 H=84 G=88

¤ Dauer der Verarbeitung: 0.26 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge