/* * ----------------------------------------------------------------------- * Copyright © 2013-2016 Meno Hochschild, <http://www.menodata.de/> * ----------------------------------------------------------------------- * This file (LookupProcessor.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 <http://www.gnu.org/licenses/>. * ----------------------------------------------------------------------- */ package net.time4j.format.expert; import net.time4j.engine.AttributeQuery; import net.time4j.engine.ChronoDisplay; import net.time4j.engine.ChronoElement; import net.time4j.format.Attributes; import java.io.IOException; import java.util.Collections; import java.util.EnumMap; import java.util.Locale; import java.util.Map; import java.util.Set; /** * <p>Text-Formatierung eines chronologischen Elements mit Hilfe von * String-Ressourcen. </p> * * @param <V> generic type of element values * @author Meno Hochschild * @since 3.0 */ final class LookupProcessor<V extends Enum<V>> implements FormatProcessor<V> { //~ Instanzvariablen ---------------------------------------------- private final ChronoElement<V> element; private final Map<V, String> resources; // quick path optimization private final int protectedLength; private final boolean caseInsensitive; private final Locale locale; //~ Konstruktoren ----------------------------------------------------- /** * <p>Konstruiert eine neue Instanz. </p> * * @param element element to be formatted * @param resources text resources */ LookupProcessor( ChronoElement<V> element, Map<V, String> resources ) { super(); Map<V, String> tmp = new EnumMap<>(element.getType()); tmp.putAll(resources); this.element = element; this.resources = Collections.unmodifiableMap(tmp); this.protectedLength = 0; this.caseInsensitive = true; this.locale = Locale.getDefault(Locale.Category.FORMAT); } private LookupProcessor( ChronoElement<V> element, Map<V, String> resources, int protectedLength, boolean caseInsensitive, Locale locale ) { super(); this.element = element; this.resources = resources; // quick path members this.protectedLength = protectedLength; this.caseInsensitive = caseInsensitive; this.locale = locale; } //~ Methoden ---------------------------------------------------------- @Override public void print( ChronoDisplay formattable, Appendable buffer, AttributeQuery attributes, Set<ElementPosition> positions, boolean quickPath ) throws IOException { if (buffer instanceof CharSequence) { CharSequence cs = (CharSequence) buffer; int offset = cs.length(); this.print(formattable, buffer); if (positions != null) { positions.add(new ElementPosition(this.element, offset, cs.length())); } } else { this.print(formattable, buffer); } } @Override public void parse( CharSequence text, ParseLog status, AttributeQuery attributes, ParsedEntity<?> parsedResult, boolean quickPath ) { int start = status.getPosition(); int len = text.length(); int protectedChars = (quickPath ? this.protectedLength : attributes.get(Attributes.PROTECTED_CHARACTERS, 0)); if (protectedChars > 0) { len -= protectedChars; } if (start >= len) { status.setError(start, "Missing chars for: " + this.element.name()); status.setWarning(); return; } boolean ignoreCase = ( quickPath ? this.caseInsensitive : attributes.get(Attributes.PARSE_CASE_INSENSITIVE, Boolean.TRUE).booleanValue()); Locale loc = ( quickPath ? this.locale : attributes.get(Attributes.LANGUAGE, Locale.getDefault(Locale.Category.FORMAT))); int maxCount = len - start; for (V value : this.element.getType().getEnumConstants()) { String test = this.getString(value); if (ignoreCase) { String upper = test.toUpperCase(loc); int count = test.length(); if (count <= maxCount) { String s = text.subSequence(start, start + count).toString().toUpperCase(loc); if (upper.equals(s)) { parsedResult.put(this.element, value); status.setPosition(start + count); return; } } } else { int count = test.length(); if (count <= maxCount) { CharSequence cs = text.subSequence(start, start + count); if (test.equals(cs.toString())) { parsedResult.put(this.element, value); status.setPosition(start + count); return; } } } } status.setError(start, "Enum value could not be parsed."); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } else if (obj instanceof LookupProcessor) { LookupProcessor<?> that = (LookupProcessor) obj; return ( this.element.equals(that.element) && this.resources.equals(that.resources) ); } else { return false; } } @Override public int hashCode() { return ( 7 * this.element.hashCode() + 31 * this.resources.hashCode() ); } @Override public String toString() { StringBuilder sb = new StringBuilder(512); sb.append(this.getClass().getName()); sb.append("[element="); sb.append(this.element.name()); sb.append(", resources="); sb.append(this.resources); sb.append(']'); return sb.toString(); } @Override public ChronoElement<V> getElement() { return this.element; } @Override public FormatProcessor<V> withElement(ChronoElement<V> element) { if (this.element == element) { return this; } return new LookupProcessor<>(element, this.resources); } @Override public boolean isNumerical() { return false; } @Override public FormatProcessor<V> quickPath( ChronoFormatter<?> formatter, AttributeQuery attributes, int reserved ) { return new LookupProcessor<>( this.element, this.resources, attributes.get(Attributes.PROTECTED_CHARACTERS, Integer.valueOf(0)).intValue(), attributes.get(Attributes.PARSE_CASE_INSENSITIVE, Boolean.TRUE).booleanValue(), attributes.get(Attributes.LANGUAGE, Locale.getDefault(Locale.Category.FORMAT)) ); } private void print( ChronoDisplay formattable, Appendable buffer ) throws IOException { V value = formattable.get(this.element); buffer.append(this.getString(value)); } private String getString(V value) { String test = this.resources.get(value); if (test == null) { // Ersatzwert, wenn keine Ressource da ist test = value.toString(); } return test; } }