localeprioritylist.h   [plain text]


// © 2019 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License

// localeprioritylist.h
// created: 2019jul11 Markus W. Scherer

#ifndef __LOCALEPRIORITYLIST_H__
#define __LOCALEPRIORITYLIST_H__

#include "unicode/utypes.h"
#include "unicode/locid.h"
#include "unicode/stringpiece.h"
#include "unicode/uobject.h"

struct UHashtable;

U_NAMESPACE_BEGIN

struct LocaleAndWeightArray;

/**
 * Parses a list of locales from an accept-language string.
 * We are a bit more lenient than the spec:
 * We accept extra whitespace in more places, empty range fields,
 * and any number of qvalue fraction digits.
 *
 * https://tools.ietf.org/html/rfc2616#section-14.4
 * 14.4 Accept-Language
 *
 *        Accept-Language = "Accept-Language" ":"
 *                          1#( language-range [ ";" "q" "=" qvalue ] )
 *        language-range  = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" )
 *
 *    Each language-range MAY be given an associated quality value which
 *    represents an estimate of the user's preference for the languages
 *    specified by that range. The quality value defaults to "q=1". For
 *    example,
 *
 *        Accept-Language: da, en-gb;q=0.8, en;q=0.7
 *
 * https://tools.ietf.org/html/rfc2616#section-3.9
 * 3.9 Quality Values
 *
 *    HTTP content negotiation (section 12) uses short "floating point"
 *    numbers to indicate the relative importance ("weight") of various
 *    negotiable parameters.  A weight is normalized to a real number in
 *    the range 0 through 1, where 0 is the minimum and 1 the maximum
 *    value. If a parameter has a quality value of 0, then content with
 *    this parameter is `not acceptable' for the client. HTTP/1.1
 *    applications MUST NOT generate more than three digits after the
 *    decimal point. User configuration of these values SHOULD also be
 *    limited in this fashion.
 *
 *        qvalue         = ( "0" [ "." 0*3DIGIT ] )
 *                       | ( "1" [ "." 0*3("0") ] )
 */
class U_COMMON_API LocalePriorityList : public UMemory {
public:
    class Iterator : public Locale::Iterator {
    public:
        UBool hasNext() const override { return count < length; }

        const Locale &next() override {
            for(;;) {
                const Locale *locale = list.localeAt(index++);
                if (locale != nullptr) {
                    ++count;
                    return *locale;
                }
            }
        }

    private:
        friend class LocalePriorityList;

        Iterator(const LocalePriorityList &list) : list(list), length(list.getLength()) {}

        const LocalePriorityList &list;
        int32_t index = 0;
        int32_t count = 0;
        const int32_t length;
    };

    LocalePriorityList(StringPiece s, UErrorCode &errorCode);

    ~LocalePriorityList();

    int32_t getLength() const { return listLength - numRemoved; }

    int32_t getLengthIncludingRemoved() const { return listLength; }

    Iterator iterator() const { return Iterator(*this); }

    const Locale *localeAt(int32_t i) const;

    Locale *orphanLocaleAt(int32_t i);

private:
    LocalePriorityList(const LocalePriorityList &) = delete;
    LocalePriorityList &operator=(const LocalePriorityList &) = delete;

    bool add(const Locale &locale, int32_t weight, UErrorCode &errorCode);

    void sort(UErrorCode &errorCode);

    LocaleAndWeightArray *list = nullptr;
    int32_t listLength = 0;
    int32_t numRemoved = 0;
    bool hasWeights = false;  // other than 1.0
    UHashtable *map = nullptr;
};

U_NAMESPACE_END

#endif  // __LOCALEPRIORITYLIST_H__