Search in sources :

Example 1 with GenericMatchInfo

use of android.icu.impl.TimeZoneGenericNames.GenericMatchInfo in project j2objc by google.

the class TimeZoneFormat method parse.

/**
 * Returns a <code>TimeZone</code> by parsing the time zone string according to
 * the parse position, the style and the parse options.
 *
 * @param text the text contains a time zone string at the position.
 * @param style the format style.
 * @param pos the position.
 * @param options the parse options.
 * @param timeType The output argument for receiving the time type (standard/daylight/unknown),
 * or specify null if the information is not necessary.
 * @return A <code>TimeZone</code>, or null if the input could not be parsed.
 * @see Style
 * @see #format(Style, TimeZone, long, Output)
 */
public TimeZone parse(Style style, String text, ParsePosition pos, EnumSet<ParseOption> options, Output<TimeType> timeType) {
    if (timeType == null) {
        timeType = new Output<TimeType>(TimeType.UNKNOWN);
    } else {
        timeType.value = TimeType.UNKNOWN;
    }
    int startIdx = pos.getIndex();
    int maxPos = text.length();
    int offset;
    // Styles using localized GMT format as fallback
    boolean fallbackLocalizedGMT = (style == Style.SPECIFIC_LONG || style == Style.GENERIC_LONG || style == Style.GENERIC_LOCATION);
    boolean fallbackShortLocalizedGMT = (style == Style.SPECIFIC_SHORT || style == Style.GENERIC_SHORT);
    // bit flags representing already evaluated styles
    int evaluated = 0;
    ParsePosition tmpPos = new ParsePosition(startIdx);
    // stores successfully parsed offset for later use
    int parsedOffset = UNKNOWN_OFFSET;
    // stores successfully parsed offset position for later use
    int parsedPos = -1;
    // Try localized GMT format first if necessary
    if (fallbackLocalizedGMT || fallbackShortLocalizedGMT) {
        Output<Boolean> hasDigitOffset = new Output<Boolean>(false);
        offset = parseOffsetLocalizedGMT(text, tmpPos, fallbackShortLocalizedGMT, hasDigitOffset);
        if (tmpPos.getErrorIndex() == -1) {
            // 2) The input text was not completely processed
            if (tmpPos.getIndex() == maxPos || hasDigitOffset.value) {
                pos.setIndex(tmpPos.getIndex());
                return getTimeZoneForOffset(offset);
            }
            parsedOffset = offset;
            parsedPos = tmpPos.getIndex();
        }
        // Note: For now, no distinction between long/short localized GMT format in the parser.
        // This might be changed in future.
        // evaluated |= (fallbackLocalizedGMT ? Style.LOCALIZED_GMT.flag : Style.LOCALIZED_GMT_SHORT.flag);
        evaluated |= (Style.LOCALIZED_GMT.flag | Style.LOCALIZED_GMT_SHORT.flag);
    }
    boolean parseTZDBAbbrev = (options == null) ? getDefaultParseOptions().contains(ParseOption.TZ_DATABASE_ABBREVIATIONS) : options.contains(ParseOption.TZ_DATABASE_ABBREVIATIONS);
    // Try the specified style
    switch(style) {
        case LOCALIZED_GMT:
            {
                tmpPos.setIndex(startIdx);
                tmpPos.setErrorIndex(-1);
                offset = parseOffsetLocalizedGMT(text, tmpPos);
                if (tmpPos.getErrorIndex() == -1) {
                    pos.setIndex(tmpPos.getIndex());
                    return getTimeZoneForOffset(offset);
                }
                // Note: For now, no distinction between long/short localized GMT format in the parser.
                // This might be changed in future.
                evaluated |= Style.LOCALIZED_GMT_SHORT.flag;
                break;
            }
        case LOCALIZED_GMT_SHORT:
            {
                tmpPos.setIndex(startIdx);
                tmpPos.setErrorIndex(-1);
                offset = parseOffsetShortLocalizedGMT(text, tmpPos);
                if (tmpPos.getErrorIndex() == -1) {
                    pos.setIndex(tmpPos.getIndex());
                    return getTimeZoneForOffset(offset);
                }
                // Note: For now, no distinction between long/short localized GMT format in the parser.
                // This might be changed in future.
                evaluated |= Style.LOCALIZED_GMT.flag;
                break;
            }
        case ISO_BASIC_SHORT:
        case ISO_BASIC_FIXED:
        case ISO_BASIC_FULL:
        case ISO_EXTENDED_FIXED:
        case ISO_EXTENDED_FULL:
            {
                tmpPos.setIndex(startIdx);
                tmpPos.setErrorIndex(-1);
                offset = parseOffsetISO8601(text, tmpPos);
                if (tmpPos.getErrorIndex() == -1) {
                    pos.setIndex(tmpPos.getIndex());
                    return getTimeZoneForOffset(offset);
                }
                break;
            }
        case ISO_BASIC_LOCAL_SHORT:
        case ISO_BASIC_LOCAL_FIXED:
        case ISO_BASIC_LOCAL_FULL:
        case ISO_EXTENDED_LOCAL_FIXED:
        case ISO_EXTENDED_LOCAL_FULL:
            {
                tmpPos.setIndex(startIdx);
                tmpPos.setErrorIndex(-1);
                // Exclude the case of UTC Indicator "Z" here
                Output<Boolean> hasDigitOffset = new Output<Boolean>(false);
                offset = parseOffsetISO8601(text, tmpPos, false, hasDigitOffset);
                if (tmpPos.getErrorIndex() == -1 && hasDigitOffset.value) {
                    pos.setIndex(tmpPos.getIndex());
                    return getTimeZoneForOffset(offset);
                }
                break;
            }
        case SPECIFIC_LONG:
        case SPECIFIC_SHORT:
            {
                // Specific styles
                EnumSet<NameType> nameTypes = null;
                if (style == Style.SPECIFIC_LONG) {
                    nameTypes = EnumSet.of(NameType.LONG_STANDARD, NameType.LONG_DAYLIGHT);
                } else {
                    assert style == Style.SPECIFIC_SHORT;
                    nameTypes = EnumSet.of(NameType.SHORT_STANDARD, NameType.SHORT_DAYLIGHT);
                }
                Collection<MatchInfo> specificMatches = _tznames.find(text, startIdx, nameTypes);
                if (specificMatches != null) {
                    MatchInfo specificMatch = null;
                    for (MatchInfo match : specificMatches) {
                        if (startIdx + match.matchLength() > parsedPos) {
                            specificMatch = match;
                            parsedPos = startIdx + match.matchLength();
                        }
                    }
                    if (specificMatch != null) {
                        timeType.value = getTimeType(specificMatch.nameType());
                        pos.setIndex(parsedPos);
                        return TimeZone.getTimeZone(getTimeZoneID(specificMatch.tzID(), specificMatch.mzID()));
                    }
                }
                if (parseTZDBAbbrev && style == Style.SPECIFIC_SHORT) {
                    assert nameTypes.contains(NameType.SHORT_STANDARD);
                    assert nameTypes.contains(NameType.SHORT_DAYLIGHT);
                    Collection<MatchInfo> tzdbNameMatches = getTZDBTimeZoneNames().find(text, startIdx, nameTypes);
                    if (tzdbNameMatches != null) {
                        MatchInfo tzdbNameMatch = null;
                        for (MatchInfo match : tzdbNameMatches) {
                            if (startIdx + match.matchLength() > parsedPos) {
                                tzdbNameMatch = match;
                                parsedPos = startIdx + match.matchLength();
                            }
                        }
                        if (tzdbNameMatch != null) {
                            timeType.value = getTimeType(tzdbNameMatch.nameType());
                            pos.setIndex(parsedPos);
                            return TimeZone.getTimeZone(getTimeZoneID(tzdbNameMatch.tzID(), tzdbNameMatch.mzID()));
                        }
                    }
                }
                break;
            }
        case GENERIC_LONG:
        case GENERIC_SHORT:
        case GENERIC_LOCATION:
            {
                EnumSet<GenericNameType> genericNameTypes = null;
                switch(style) {
                    case GENERIC_LOCATION:
                        genericNameTypes = EnumSet.of(GenericNameType.LOCATION);
                        break;
                    case GENERIC_LONG:
                        genericNameTypes = EnumSet.of(GenericNameType.LONG, GenericNameType.LOCATION);
                        break;
                    case GENERIC_SHORT:
                        genericNameTypes = EnumSet.of(GenericNameType.SHORT, GenericNameType.LOCATION);
                        break;
                    default:
                        // style cannot be other than above cases
                        assert false;
                        break;
                }
                GenericMatchInfo bestGeneric = getTimeZoneGenericNames().findBestMatch(text, startIdx, genericNameTypes);
                if (bestGeneric != null && (startIdx + bestGeneric.matchLength() > parsedPos)) {
                    timeType.value = bestGeneric.timeType();
                    pos.setIndex(startIdx + bestGeneric.matchLength());
                    return TimeZone.getTimeZone(bestGeneric.tzID());
                }
                break;
            }
        case ZONE_ID:
            {
                tmpPos.setIndex(startIdx);
                tmpPos.setErrorIndex(-1);
                String id = parseZoneID(text, tmpPos);
                if (tmpPos.getErrorIndex() == -1) {
                    pos.setIndex(tmpPos.getIndex());
                    return TimeZone.getTimeZone(id);
                }
                break;
            }
        case ZONE_ID_SHORT:
            {
                tmpPos.setIndex(startIdx);
                tmpPos.setErrorIndex(-1);
                String id = parseShortZoneID(text, tmpPos);
                if (tmpPos.getErrorIndex() == -1) {
                    pos.setIndex(tmpPos.getIndex());
                    return TimeZone.getTimeZone(id);
                }
                break;
            }
        case EXEMPLAR_LOCATION:
            {
                tmpPos.setIndex(startIdx);
                tmpPos.setErrorIndex(-1);
                String id = parseExemplarLocation(text, tmpPos);
                if (tmpPos.getErrorIndex() == -1) {
                    pos.setIndex(tmpPos.getIndex());
                    return TimeZone.getTimeZone(id);
                }
                break;
            }
    }
    evaluated |= style.flag;
    if (parsedPos > startIdx) {
        // GMT format.
        assert parsedOffset != UNKNOWN_OFFSET;
        pos.setIndex(parsedPos);
        return getTimeZoneForOffset(parsedOffset);
    }
    // Failed to parse the input text as the time zone format in the specified style.
    // Check the longest match among other styles below.
    // stores successfully parsed zone ID for later use
    String parsedID = null;
    // stores successfully parsed time type for later use
    TimeType parsedTimeType = TimeType.UNKNOWN;
    assert parsedPos < 0;
    assert parsedOffset == UNKNOWN_OFFSET;
    // ISO 8601
    if (parsedPos < maxPos && ((evaluated & ISO_Z_STYLE_FLAG) == 0 || (evaluated & ISO_LOCAL_STYLE_FLAG) == 0)) {
        tmpPos.setIndex(startIdx);
        tmpPos.setErrorIndex(-1);
        Output<Boolean> hasDigitOffset = new Output<Boolean>(false);
        offset = parseOffsetISO8601(text, tmpPos, false, hasDigitOffset);
        if (tmpPos.getErrorIndex() == -1) {
            if (tmpPos.getIndex() == maxPos || hasDigitOffset.value) {
                pos.setIndex(tmpPos.getIndex());
                return getTimeZoneForOffset(offset);
            }
            // may collide with other names. In this case, we need to evaluate other names.
            if (parsedPos < tmpPos.getIndex()) {
                parsedOffset = offset;
                parsedID = null;
                parsedTimeType = TimeType.UNKNOWN;
                parsedPos = tmpPos.getIndex();
                // only when "Z" is used
                assert parsedPos == startIdx + 1;
            }
        }
    }
    // Localized GMT format
    if (parsedPos < maxPos && (evaluated & Style.LOCALIZED_GMT.flag) == 0) {
        tmpPos.setIndex(startIdx);
        tmpPos.setErrorIndex(-1);
        Output<Boolean> hasDigitOffset = new Output<Boolean>(false);
        offset = parseOffsetLocalizedGMT(text, tmpPos, false, hasDigitOffset);
        if (tmpPos.getErrorIndex() == -1) {
            if (tmpPos.getIndex() == maxPos || hasDigitOffset.value) {
                pos.setIndex(tmpPos.getIndex());
                return getTimeZoneForOffset(offset);
            }
            // Evaluate other names - see the comment earlier in this method.
            if (parsedPos < tmpPos.getIndex()) {
                parsedOffset = offset;
                parsedID = null;
                parsedTimeType = TimeType.UNKNOWN;
                parsedPos = tmpPos.getIndex();
            }
        }
    }
    if (parsedPos < maxPos && (evaluated & Style.LOCALIZED_GMT_SHORT.flag) == 0) {
        tmpPos.setIndex(startIdx);
        tmpPos.setErrorIndex(-1);
        Output<Boolean> hasDigitOffset = new Output<Boolean>(false);
        offset = parseOffsetLocalizedGMT(text, tmpPos, true, hasDigitOffset);
        if (tmpPos.getErrorIndex() == -1) {
            if (tmpPos.getIndex() == maxPos || hasDigitOffset.value) {
                pos.setIndex(tmpPos.getIndex());
                return getTimeZoneForOffset(offset);
            }
            // Evaluate other names - see the comment earlier in this method.
            if (parsedPos < tmpPos.getIndex()) {
                parsedOffset = offset;
                parsedID = null;
                parsedTimeType = TimeType.UNKNOWN;
                parsedPos = tmpPos.getIndex();
            }
        }
    }
    // When ParseOption.ALL_STYLES is available, we also try to look all possible display names and IDs.
    // For example, when style is GENERIC_LONG, "EST" (SPECIFIC_SHORT) is never
    // used for America/New_York. With parseAllStyles true, this code parses "EST"
    // as America/New_York.
    // Note: Adding all possible names into the trie used by the implementation is quite heavy operation,
    // which we want to avoid normally (note that we cache the trie, so this is applicable to the
    // first time only as long as the cache does not expire).
    boolean parseAllStyles = (options == null) ? getDefaultParseOptions().contains(ParseOption.ALL_STYLES) : options.contains(ParseOption.ALL_STYLES);
    if (parseAllStyles) {
        // Try all specific names and exemplar location names
        if (parsedPos < maxPos) {
            Collection<MatchInfo> specificMatches = _tznames.find(text, startIdx, ALL_SIMPLE_NAME_TYPES);
            MatchInfo specificMatch = null;
            int matchPos = -1;
            if (specificMatches != null) {
                for (MatchInfo match : specificMatches) {
                    if (startIdx + match.matchLength() > matchPos) {
                        specificMatch = match;
                        matchPos = startIdx + match.matchLength();
                    }
                }
            }
            if (parsedPos < matchPos) {
                parsedPos = matchPos;
                parsedID = getTimeZoneID(specificMatch.tzID(), specificMatch.mzID());
                parsedTimeType = getTimeType(specificMatch.nameType());
                parsedOffset = UNKNOWN_OFFSET;
            }
        }
        if (parseTZDBAbbrev && parsedPos < maxPos && (evaluated & Style.SPECIFIC_SHORT.flag) == 0) {
            Collection<MatchInfo> tzdbNameMatches = getTZDBTimeZoneNames().find(text, startIdx, ALL_SIMPLE_NAME_TYPES);
            MatchInfo tzdbNameMatch = null;
            int matchPos = -1;
            if (tzdbNameMatches != null) {
                for (MatchInfo match : tzdbNameMatches) {
                    if (startIdx + match.matchLength() > matchPos) {
                        tzdbNameMatch = match;
                        matchPos = startIdx + match.matchLength();
                    }
                }
                if (parsedPos < matchPos) {
                    parsedPos = matchPos;
                    parsedID = getTimeZoneID(tzdbNameMatch.tzID(), tzdbNameMatch.mzID());
                    parsedTimeType = getTimeType(tzdbNameMatch.nameType());
                    parsedOffset = UNKNOWN_OFFSET;
                }
            }
        }
        // Try generic names
        if (parsedPos < maxPos) {
            GenericMatchInfo genericMatch = getTimeZoneGenericNames().findBestMatch(text, startIdx, ALL_GENERIC_NAME_TYPES);
            if (genericMatch != null && parsedPos < startIdx + genericMatch.matchLength()) {
                parsedPos = startIdx + genericMatch.matchLength();
                parsedID = genericMatch.tzID();
                parsedTimeType = genericMatch.timeType();
                parsedOffset = UNKNOWN_OFFSET;
            }
        }
        // Try time zone ID
        if (parsedPos < maxPos && (evaluated & Style.ZONE_ID.flag) == 0) {
            tmpPos.setIndex(startIdx);
            tmpPos.setErrorIndex(-1);
            String id = parseZoneID(text, tmpPos);
            if (tmpPos.getErrorIndex() == -1 && parsedPos < tmpPos.getIndex()) {
                parsedPos = tmpPos.getIndex();
                parsedID = id;
                parsedTimeType = TimeType.UNKNOWN;
                parsedOffset = UNKNOWN_OFFSET;
            }
        }
        // Try short time zone ID
        if (parsedPos < maxPos && (evaluated & Style.ZONE_ID_SHORT.flag) == 0) {
            tmpPos.setIndex(startIdx);
            tmpPos.setErrorIndex(-1);
            String id = parseShortZoneID(text, tmpPos);
            if (tmpPos.getErrorIndex() == -1 && parsedPos < tmpPos.getIndex()) {
                parsedPos = tmpPos.getIndex();
                parsedID = id;
                parsedTimeType = TimeType.UNKNOWN;
                parsedOffset = UNKNOWN_OFFSET;
            }
        }
    }
    if (parsedPos > startIdx) {
        // Parsed successfully
        TimeZone parsedTZ = null;
        if (parsedID != null) {
            parsedTZ = TimeZone.getTimeZone(parsedID);
        } else {
            assert parsedOffset != UNKNOWN_OFFSET;
            parsedTZ = getTimeZoneForOffset(parsedOffset);
        }
        timeType.value = parsedTimeType;
        pos.setIndex(parsedPos);
        return parsedTZ;
    }
    pos.setErrorIndex(startIdx);
    return null;
}
Also used : GenericMatchInfo(android.icu.impl.TimeZoneGenericNames.GenericMatchInfo) EnumSet(java.util.EnumSet) AttributedString(java.text.AttributedString) TimeZone(android.icu.util.TimeZone) GenericMatchInfo(android.icu.impl.TimeZoneGenericNames.GenericMatchInfo) MatchInfo(android.icu.text.TimeZoneNames.MatchInfo) Output(android.icu.util.Output) Collection(java.util.Collection) ParsePosition(java.text.ParsePosition)

Aggregations

GenericMatchInfo (android.icu.impl.TimeZoneGenericNames.GenericMatchInfo)1 MatchInfo (android.icu.text.TimeZoneNames.MatchInfo)1 Output (android.icu.util.Output)1 TimeZone (android.icu.util.TimeZone)1 AttributedString (java.text.AttributedString)1 ParsePosition (java.text.ParsePosition)1 Collection (java.util.Collection)1 EnumSet (java.util.EnumSet)1