| // © 2018 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html#License |
| package com.ibm.icu.impl.number.parse; |
| |
| import com.ibm.icu.impl.number.AffixUtils; |
| |
| /** |
| * A specialized version of {@link SeriesMatcher} that matches EITHER a prefix OR a suffix. |
| * {@link AffixMatcher} combines two of these in order to match both the prefix and suffix. |
| * |
| * @author sffc |
| */ |
| public class AffixPatternMatcher extends SeriesMatcher implements AffixUtils.TokenConsumer { |
| |
| private final String affixPattern; |
| |
| // Used during construction only: |
| private MatcherFactory factory; |
| private IgnorablesMatcher ignorables; |
| private int lastTypeOrCp; |
| |
| private AffixPatternMatcher(String affixPattern) { |
| this.affixPattern = affixPattern; |
| } |
| |
| /** |
| * Creates an AffixPatternMatcher (based on SeriesMatcher) from the given affix pattern. Returns null |
| * if the affix pattern is empty. |
| */ |
| public static AffixPatternMatcher fromAffixPattern( |
| String affixPattern, |
| MatcherFactory factory, |
| int parseFlags) { |
| if (affixPattern.isEmpty()) { |
| return null; |
| } |
| |
| AffixPatternMatcher series = new AffixPatternMatcher(affixPattern); |
| series.factory = factory; |
| series.ignorables = (0 != (parseFlags & ParsingUtils.PARSE_FLAG_EXACT_AFFIX)) ? null |
| : factory.ignorables(); |
| series.lastTypeOrCp = 0; |
| AffixUtils.iterateWithConsumer(affixPattern, series); |
| |
| // De-reference the memory |
| series.factory = null; |
| series.ignorables = null; |
| series.lastTypeOrCp = 0; |
| |
| series.freeze(); |
| return series; |
| } |
| |
| /** |
| * This method is NOT intended to be called directly. It is here for the AffixUtils.TokenConsumer |
| * interface only. |
| */ |
| @Override |
| public void consumeToken(int typeOrCp) { |
| // This is called by AffixUtils.iterateWithConsumer() for each token. |
| |
| // Add an ignorables matcher between tokens except between two literals, and don't put two |
| // ignorables matchers in a row. |
| if (ignorables != null |
| && length() > 0 |
| && (lastTypeOrCp < 0 || !ignorables.getSet().contains(lastTypeOrCp))) { |
| addMatcher(ignorables); |
| } |
| |
| if (typeOrCp < 0) { |
| // Case 1: the token is a symbol. |
| switch (typeOrCp) { |
| case AffixUtils.TYPE_MINUS_SIGN: |
| addMatcher(factory.minusSign(true)); |
| break; |
| case AffixUtils.TYPE_PLUS_SIGN: |
| addMatcher(factory.plusSign(true)); |
| break; |
| case AffixUtils.TYPE_PERCENT: |
| addMatcher(factory.percent()); |
| break; |
| case AffixUtils.TYPE_PERMILLE: |
| addMatcher(factory.permille()); |
| break; |
| case AffixUtils.TYPE_CURRENCY_SINGLE: |
| case AffixUtils.TYPE_CURRENCY_DOUBLE: |
| case AffixUtils.TYPE_CURRENCY_TRIPLE: |
| case AffixUtils.TYPE_CURRENCY_QUAD: |
| case AffixUtils.TYPE_CURRENCY_QUINT: |
| // All currency symbols use the same matcher |
| addMatcher(factory.currency()); |
| break; |
| default: |
| throw new AssertionError(); |
| } |
| |
| } else if (ignorables != null && ignorables.getSet().contains(typeOrCp)) { |
| // Case 2: the token is an ignorable literal. |
| // No action necessary: the ignorables matcher has already been added. |
| |
| } else { |
| // Case 3: the token is a non-ignorable literal. |
| addMatcher(CodePointMatcher.getInstance(typeOrCp)); |
| } |
| lastTypeOrCp = typeOrCp; |
| } |
| |
| public String getPattern() { |
| return affixPattern; |
| } |
| |
| @Override |
| public boolean equals(Object other) { |
| if (this == other) |
| return true; |
| if (!(other instanceof AffixPatternMatcher)) |
| return false; |
| return affixPattern.equals(((AffixPatternMatcher) other).affixPattern); |
| } |
| |
| @Override |
| public int hashCode() { |
| return affixPattern.hashCode(); |
| } |
| |
| @Override |
| public String toString() { |
| return affixPattern; |
| } |
| } |