net.time4j.format.TextAccessor Maven / Gradle / Ivy
/*
* -----------------------------------------------------------------------
* Copyright © 2013-2016 Meno Hochschild,
* -----------------------------------------------------------------------
* This file (TextAccessor.java) is part of project Time4J.
*
* Time4J is free software: You can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* Time4J is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Time4J. If not, see .
* -----------------------------------------------------------------------
*/
package net.time4j.format;
import net.time4j.engine.AttributeQuery;
import java.text.ParsePosition;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
/**
* Supplies an access to the internal name list of an enum-based
* element value.
*
* @author Meno Hochschild
* @since 2.0
* @see CalendarText
* @doctags.concurrency {immutable}
*/
/*[deutsch]
* Stellt einen Zugriff auf die enthaltenen Namen per Elementwert-Enum
* bereit.
*
* @author Meno Hochschild
* @since 2.0
* @see CalendarText
* @doctags.concurrency {immutable}
*/
public final class TextAccessor {
//~ Instanzvariablen --------------------------------------------------
private final List textForms;
// private final Locale locale;
//~ Konstruktoren -----------------------------------------------------
/**
* Standard-Konstruktor.
*
* @param textForms Array von Textformen
* @param locale Sprache
*/
TextAccessor(
String[] textForms,
Locale locale
) {
super();
this.textForms =
Collections.unmodifiableList(Arrays.asList(textForms));
// this.locale = locale;
}
//~ Methoden ----------------------------------------------------------
/**
* Prints the given element value as String.
*
* If the element value has no localized representation then this
* method will simply print the enum name.
*
* @param value current value of element
* @return localized text form
*/
/*[deutsch]
* Stellt den angegebenen Elementwert als String dar.
*
* Hat der Elementwert keine lokalisierte Darstellung, wird einfach
* sein Enum-Name ausgegeben.
*
* @param value current value of element
* @return localized text form
*/
public String print(Enum> value) {
int index = value.ordinal();
if (this.textForms.size() <= index) {
return value.name();
} else {
return this.textForms.get(index);
}
}
/**
* Interpretes given text form as enum-based element value.
*
* Parsing is case-insensitive. No partial compare is performed,
* instead the whole element text will be evaluated.
*
* @param generic value type of element
* @param parseable text to be parsed
* @param status current parsing position
* @param valueType value class of element
* @return element value (as enum) or {@code null} if not found
* @see #parse(CharSequence, ParsePosition, Class, AttributeQuery)
*/
/*[deutsch]
* Interpretiert die angegebene Textform als Enum-Elementwert.
*
* Die Groß- und Kleinschreibung ist nicht relevant. Es
* wird immer jeweils der ganze Text verglichen.
*
* @param generic value type of element
* @param parseable text to be parsed
* @param status current parsing position
* @param valueType value class of element
* @return element value (as enum) or {@code null} if not found
* @see #parse(CharSequence, ParsePosition, Class, AttributeQuery)
*/
public > V parse(
CharSequence parseable,
ParsePosition status,
Class valueType
) {
return this.parse(parseable, status, valueType, true, false);
}
/**
* Interpretes given text form as enum-based element value.
*
* @param generic value type of element
* @param parseable text to be parsed
* @param status current parsing position
* @param valueType value class of element
* @param leniency leniency mode
* @return element value (as enum) or {@code null} if not found
* @see Attributes#LENIENCY
* @see #parse(CharSequence, ParsePosition, Class, AttributeQuery)
* @since 3.15/4.12
*/
/*[deutsch]
* Interpretiert die angegebene Textform als Enum-Elementwert.
*
* @param generic value type of element
* @param parseable text to be parsed
* @param status current parsing position
* @param valueType value class of element
* @param leniency leniency mode
* @return element value (as enum) or {@code null} if not found
* @see Attributes#LENIENCY
* @see #parse(CharSequence, ParsePosition, Class, AttributeQuery)
* @since 3.15/4.12
*/
public > V parse(
CharSequence parseable,
ParsePosition status,
Class valueType,
Leniency leniency
) {
boolean caseInsensitive = true;
boolean partialCompare = false;
if (leniency == Leniency.STRICT) {
caseInsensitive = false;
} else if (leniency == Leniency.LAX) {
partialCompare = true;
}
return this.parse(parseable, status, valueType, caseInsensitive, partialCompare);
}
/**
* Interpretes given text form as enum-based element value.
*
* The attributes {@code Attributes.PARSE_CASE_INSENSITIVE} and
* {@code Attributes.PARSE_PARTIAL_COMPARE} will be evaluated.
*
* @param generic value type of element
* @param parseable text to be parsed
* @param status current parsing position
* @param valueType value class of element
* @param attributes format attributes
* @return element value (as enum) or {@code null} if not found
* @see Attributes#PARSE_CASE_INSENSITIVE
* @see Attributes#PARSE_PARTIAL_COMPARE
*/
/*[deutsch]
* Interpretiert die angegebene Textform als Enum-Elementwert.
*
* Es werden die Attribute {@code Attributes.PARSE_CASE_INSENSITIVE}
* und {@code Attributes.PARSE_PARTIAL_COMPARE} ausgewertet.
*
* @param generic value type of element
* @param parseable text to be parsed
* @param status current parsing position
* @param valueType value class of element
* @param attributes format attributes
* @return element value (as enum) or {@code null} if not found
* @see Attributes#PARSE_CASE_INSENSITIVE
* @see Attributes#PARSE_PARTIAL_COMPARE
*/
public > V parse(
CharSequence parseable,
ParsePosition status,
Class valueType,
AttributeQuery attributes
) {
boolean caseInsensitive =
attributes
.get(Attributes.PARSE_CASE_INSENSITIVE, Boolean.TRUE)
.booleanValue();
boolean partialCompare =
attributes
.get(Attributes.PARSE_PARTIAL_COMPARE, Boolean.FALSE)
.booleanValue();
return this.parse(
parseable, status, valueType, caseInsensitive, partialCompare);
}
/**
* Supports mainly debugging.
*/
/*[deutsch]
* Dient im wesentlichen Debugging-Zwecken.
*/
@Override
public String toString() {
int n = this.textForms.size();
StringBuilder sb = new StringBuilder(n * 16 + 2);
sb.append('{');
boolean first = true;
for (int i = 0; i < n; i++) {
if (first) {
first = false;
} else {
sb.append(',');
}
sb.append(this.textForms.get(i));
}
sb.append('}');
return sb.toString();
}
private > V parse(
CharSequence parseable,
ParsePosition status,
Class valueType,
boolean caseInsensitive,
boolean partialCompare
) {
V[] enums = valueType.getEnumConstants();
int len = this.textForms.size();
int start = status.getIndex();
int end = parseable.length();
int maxEq = 0;
V candidate = null;
for (int i = 0; i < enums.length; i++) {
String s = ((i >= len) ? enums[i].name() : this.textForms.get(i));
int pos = start;
int n = s.length();
boolean eq = true;
for (int j = 0; eq && (j < n); j++) {
if (start + j >= end) {
eq = false;
} else {
char c = parseable.charAt(start + j);
char t = s.charAt(j);
if (caseInsensitive) {
eq = (c == t) || this.compareIgnoreCase(c, t);
} else {
eq = (c == t);
}
if (eq) {
pos++;
}
}
}
if (partialCompare || (n == 1)) {
if (maxEq < pos - start) {
maxEq = pos - start;
candidate = enums[i];
} else if (maxEq == pos - start) {
candidate = null;
}
} else if (eq) {
assert pos == start + n;
status.setIndex(pos);
return enums[i];
}
}
if (candidate == null) {
status.setErrorIndex(start);
} else {
status.setIndex(start + maxEq);
}
return candidate;
}
private boolean compareIgnoreCase(char c1, char c2) {
if (c1 >= 'a' && c1 <= 'z') {
if (c2 >= 'A' && c2 <= 'Z') {
c2 = (char) (c2 + 'a' - 'A');
}
return (c1 == c2);
} else if (c1 >= 'A' && c1 <= 'Z') {
c1 = (char) (c1 + 'a' - 'A');
if (c2 >= 'A' && c2 <= 'Z') {
c2 = (char) (c2 + 'a' - 'A');
}
return (c1 == c2);
}
return (
Character.toUpperCase(c1) == Character.toUpperCase(c2)
|| Character.toLowerCase(c1) == Character.toLowerCase(c2)
);
// old code
// String s1 = String.valueOf(c1).toUpperCase(this.locale);
// String s2 = String.valueOf(c2).toUpperCase(this.locale);
// return s1.equals(s2);
}
}